Documentation
¶
Overview ¶
errbox boxes iter.Seq[V, error] and converts to iter.Seq[V]. The occurrence of the error stops the boxed iterator. The error can be later inspected through method.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Box ¶
type Box[V any] struct { // contains filtered or unexported fields }
Box boxes an input iter.Seq2[V, error] to iter.Seq[V] by stripping nil errors from the input iterator. The first non-nil error causes the boxed iterator to be stopped and Box stores the error. Later the error can be examined by *Box.Err.
Box converts input iterator stateful form by calling iter.Pull2. *Box.IntoIter returns the iterator as iter.Seq[V]. While consuming values from the iterator, it might conditionally yield a non-nil error. In that case Box stores the error and stops without yielding the value V paired to the error. *Box.Err returns that error otherwise nil.
The zero Box is invalid and it must be allocated by New.
func New ¶
New returns a newly allocated Box.
When a pair from seq contains non-nil error, Box discards a former value of that pair(V), then the iterator returned from Box.IntoIter stops.
*Box.Stop must be called to release resource regardless of usage.
func (*Box[V]) Err ¶
Err returns an error the input iterator has returned. If the iterator has not yet encountered an error, Err returns nil.
func (*Box[V]) IntoIter ¶ added in v0.0.11
IntoIter returns an iterator which yields values from the input iterator.
As the name IntoIter suggests, the iterator is stateful; breaking and calling seq again continues its iteration without replaying data. If the iterator finds an error, it stops iteration and will no longer produce any data. In that case the error can be inspected by calling *Box.Err.
func (*Box[V]) Stop ¶
func (b *Box[V]) Stop()
Stop releases resources allocated by New. After calling Stop, iterators returned from Box.IntoIter produce no more data.
type JsonDecoder ¶
func NewJsonDecoder ¶
func NewJsonDecoder(dec *json.Decoder) *JsonDecoder
Example (A_semantically_broken) ¶
ExampleNewJsonDecoder_a_semantically_broken demonstrates raw decoder can be accessed while iterating over tokens. Also calling Decode is safe and not a race condition. Failing to decode does not affect its iteration. After the iterator stops, no error is stored.
package main import ( "encoding/json" "fmt" "io" "strings" "github.com/ngicks/go-iterator-helper/hiter/errbox" ) func main() { const semanticallyBroken = `{ "foo": "bar", "baz": ["yay", "nay", 5, "wow"] }` dec := errbox.NewJsonDecoder(json.NewDecoder(strings.NewReader(semanticallyBroken))) defer dec.Stop() var depth int for t := range dec.IntoIter() { if depth == 1 && t == "baz" { // read opening [. t, err := dec.Dec.Token() if err != nil { panic(err) } if t != json.Delim('[') { panic("??") } var yayyay string for dec.Dec.More() { err = dec.Dec.Decode(&yayyay) if err == nil { fmt.Printf("yay? = %s\n", yayyay) } else { fmt.Printf("yay err = %v\n", err) } } // read closing ]. t, err = dec.Dec.Token() if err != nil { panic(err) } if t != json.Delim(']') { panic("??") } } switch t { case json.Delim('{'), json.Delim('['): depth++ case json.Delim('}'), json.Delim(']'): depth-- } } fmt.Printf("stored error: %v\n", dec.Err()) _, err := dec.Dec.Token() fmt.Printf("eof: %t\n", err == io.EOF) }
Output: yay? = yay yay? = nay yay err = json: cannot unmarshal number into Go value of type string yay? = wow stored error: <nil> eof: true
Example (B_syntactically_broken) ¶
ExampleNewJsonDecoder_b_syntactically_broken demonstrates that syntactically broken json inputs cause no error on reading. Also it works well with some reader implementation where final non-empty data come with io.EOF error.
package main import ( "encoding/json" "fmt" "io" "strings" "testing/iotest" "github.com/ngicks/go-iterator-helper/hiter/errbox" ) func main() { const syntacticallyBroken = `{ "foo": "bar", "baz": ["yay", "nay", 5, "wow"], "broken": { }` dec := errbox.NewJsonDecoder( json.NewDecoder( iotest.DataErrReader( strings.NewReader(syntacticallyBroken), ), ), ) defer dec.Stop() for t := range dec.IntoIter() { fmt.Printf("%v\n", t) } fmt.Printf("stored error: %v\n", dec.Err()) _, err := dec.Dec.Token() fmt.Printf("eof: %t\n", err == io.EOF) }
Output: { foo bar baz [ yay nay 5 wow ] broken { } stored error: <nil> eof: true
Example (C_reader_broken) ¶
ExampleNewJsonDecoder_c_reader_broken demonstrates an error returned from the decoder can be inspected through Err method.
package main import ( "encoding/json" "errors" "fmt" "io" "strings" "testing/iotest" "github.com/ngicks/go-iterator-helper/hiter/errbox" ) func main() { const readerBroken = `{ "foo": "bar", "baz": ["yay", "nay", 5, "wow"]` dec := errbox.NewJsonDecoder( json.NewDecoder( io.MultiReader( strings.NewReader(readerBroken), iotest.ErrReader(errors.New("sample")), ), ), ) defer dec.Stop() for t := range dec.IntoIter() { fmt.Printf("%v\n", t) } fmt.Printf("stored error: %v\n", dec.Err()) }
Output: { foo bar baz [ yay nay 5 wow ] stored error: sample
type SqlRows ¶
func NewSqlRows ¶
Example (Row_error) ¶
package main import ( "fmt" "github.com/ngicks/go-iterator-helper/hiter/errbox" "github.com/ngicks/go-iterator-helper/internal/testhelper" ) func main() { mock := testhelper.OpenMockDB(true) defer mock.Close() boxed := errbox.NewSqlRows(testhelper.QueryRows(mock), testhelper.Scan) defer boxed.Stop() for row := range boxed.IntoIter() { fmt.Printf("row = %#v\n", row) } fmt.Printf("stored err: %v\n", boxed.Err()) }
Output: row = testhelper.TestRow{Id:1, Title:"post 1", Body:"hello"} row = testhelper.TestRow{Id:2, Title:"post 2", Body:"world"} stored err: mock error
Example (Scan_error) ¶
package main import ( "database/sql" "errors" "fmt" "github.com/ngicks/go-iterator-helper/hiter/errbox" "github.com/ngicks/go-iterator-helper/internal/testhelper" ) func main() { scanErr := errors.New("scan") mock := testhelper.OpenMockDB(true) defer mock.Close() var count int boxed := errbox.NewSqlRows( testhelper.QueryRows(mock), func(r *sql.Rows) (testhelper.TestRow, error) { count++ if count > 1 { return *new(testhelper.TestRow), scanErr } return testhelper.Scan(r) }, ) defer boxed.Stop() for row := range boxed.IntoIter() { fmt.Printf("row = %#v\n", row) } fmt.Printf("stored err: %v\n", boxed.Err()) }
Output: row = testhelper.TestRow{Id:1, Title:"post 1", Body:"hello"} stored err: scan
Example (Successful) ¶
package main import ( "database/sql" "fmt" "github.com/ngicks/go-iterator-helper/hiter/errbox" "github.com/ngicks/go-iterator-helper/internal/testhelper" ) func main() { type TestRow struct { Id int Title string Body string } mock := testhelper.OpenMockDB(false) defer mock.Close() rows, err := mock.Query("SELECT id, title, body FROM posts") if err != nil { panic(err) } scanner := func(r *sql.Rows) (TestRow, error) { var t TestRow err := r.Scan(&t.Id, &t.Title, &t.Body) return t, err } boxed := errbox.NewSqlRows(rows, scanner) defer boxed.Stop() for row := range boxed.IntoIter() { fmt.Printf("row = %#v\n", row) } fmt.Printf("stored err: %v\n", boxed.Err()) }
Output: row = errbox_test.TestRow{Id:1, Title:"post 1", Body:"hello"} row = errbox_test.TestRow{Id:2, Title:"post 2", Body:"world"} row = errbox_test.TestRow{Id:3, Title:"post 3", Body:"iter"} stored err: <nil>
type XmlDecoder ¶
func NewXmlDecoder ¶
func NewXmlDecoder(dec *xml.Decoder) *XmlDecoder
Example (A_semantically_broken) ¶
ExampleNewXmlDecoder_a_semantically_broken demonstrates raw decoder can be accessed while iterating over tokens. Also calling DecodeElement is safe and not a race condition. Failing to decode does not affect its iteration. After the iterator stops, no error is stored.
package main import ( "encoding/xml" "fmt" "io" "strings" "github.com/ngicks/go-iterator-helper/hiter/errbox" ) func main() { const semanticallyBroken = ` <root> <self/> <foo>bar</foo> <baz>5</baz> <baz>23</baz> <baz>yay</baz> <baz>49</baz> </root>` dec := errbox.NewXmlDecoder(xml.NewDecoder(strings.NewReader(strings.TrimSpace(semanticallyBroken)))) defer dec.Stop() var depth int for t := range dec.IntoIter() { var ok bool tok, ok := t.(xml.StartElement) if ok { if depth == 1 && tok.Name.Local == "baz" { var yayyay int err := dec.Dec.DecodeElement(&yayyay, &tok) if err == nil { fmt.Printf("yay? = %d\n", yayyay) } else { fmt.Printf("yay err = %v\n", err) } continue } depth++ } _, ok = t.(xml.EndElement) if ok { depth-- } } fmt.Printf("stored error: %v\n", dec.Err()) _, err := dec.Dec.Token() fmt.Printf("eof: %t\n", err == io.EOF) }
Output: yay? = 5 yay? = 23 yay err = strconv.ParseInt: parsing "yay": invalid syntax yay? = 49 stored error: <nil> eof: true
Example (B_syntactically_broken) ¶
ExampleNewXmlDecoder_b_syntactically_broken demonstrates that syntactically broken xml inputs cause io.UnexpectedEOF error. Also it works well with some reader implementation where final non-empty data come with io.EOF error.
package main import ( "encoding/xml" "fmt" "strings" "testing/iotest" "github.com/ngicks/go-iterator-helper/hiter/errbox" ) func main() { const syntacticallyBroken = ` <root> <self/> <foo>bar</foo> <baz>5</baz> <baz>23</baz> <baz>yay</baz> <baz>49` dec := errbox.NewXmlDecoder( xml.NewDecoder( iotest.DataErrReader( strings.NewReader(strings.TrimSpace(syntacticallyBroken)), ), ), ) defer dec.Stop() for t := range dec.IntoIter() { fmt.Printf("%#v\n", t) } fmt.Printf("stored err: %v\n", dec.Err()) }
Output: xml.StartElement{Name:xml.Name{Space:"", Local:"root"}, Attr:[]xml.Attr{}} xml.CharData{0xa, 0x9, 0x9} xml.StartElement{Name:xml.Name{Space:"", Local:"self"}, Attr:[]xml.Attr{}} xml.EndElement{Name:xml.Name{Space:"", Local:"self"}} xml.CharData{0xa, 0x9, 0x9} xml.StartElement{Name:xml.Name{Space:"", Local:"foo"}, Attr:[]xml.Attr{}} xml.CharData{0x62, 0x61, 0x72} xml.EndElement{Name:xml.Name{Space:"", Local:"foo"}} xml.CharData{0xa, 0x9, 0x9} xml.StartElement{Name:xml.Name{Space:"", Local:"baz"}, Attr:[]xml.Attr{}} xml.CharData{0x35} xml.EndElement{Name:xml.Name{Space:"", Local:"baz"}} xml.CharData{0xa, 0x9, 0x9} xml.StartElement{Name:xml.Name{Space:"", Local:"baz"}, Attr:[]xml.Attr{}} xml.CharData{0x32, 0x33} xml.EndElement{Name:xml.Name{Space:"", Local:"baz"}} xml.CharData{0xa, 0x9, 0x9} xml.StartElement{Name:xml.Name{Space:"", Local:"baz"}, Attr:[]xml.Attr{}} xml.CharData{0x79, 0x61, 0x79} xml.EndElement{Name:xml.Name{Space:"", Local:"baz"}} xml.CharData{0xa, 0x9, 0x9} xml.StartElement{Name:xml.Name{Space:"", Local:"baz"}, Attr:[]xml.Attr{}} xml.CharData{0x34, 0x39} stored err: XML syntax error on line 7: unexpected EOF
Example (C_reader_broken) ¶
ExampleNewXmlDecoder_c_reader_broken demonstrates an error returned from the decoder can be inspected through Err method.
package main import ( "encoding/xml" "errors" "fmt" "io" "strings" "testing/iotest" "github.com/ngicks/go-iterator-helper/hiter/errbox" ) func main() { const readerBroken = ` <root> <self/> <foo>bar</foo> <baz>5</baz> <baz>23</baz> <baz>yay</baz> <baz>49` dec := errbox.NewXmlDecoder( xml.NewDecoder( io.MultiReader( strings.NewReader(strings.TrimSpace(readerBroken)), iotest.ErrReader(errors.New("sample")), ), ), ) defer dec.Stop() for t := range dec.IntoIter() { fmt.Printf("%#v\n", t) } fmt.Printf("stored err: %v\n", dec.Err()) }
Output: xml.StartElement{Name:xml.Name{Space:"", Local:"root"}, Attr:[]xml.Attr{}} xml.CharData{0xa, 0x9, 0x9} xml.StartElement{Name:xml.Name{Space:"", Local:"self"}, Attr:[]xml.Attr{}} xml.EndElement{Name:xml.Name{Space:"", Local:"self"}} xml.CharData{0xa, 0x9, 0x9} xml.StartElement{Name:xml.Name{Space:"", Local:"foo"}, Attr:[]xml.Attr{}} xml.CharData{0x62, 0x61, 0x72} xml.EndElement{Name:xml.Name{Space:"", Local:"foo"}} xml.CharData{0xa, 0x9, 0x9} xml.StartElement{Name:xml.Name{Space:"", Local:"baz"}, Attr:[]xml.Attr{}} xml.CharData{0x35} xml.EndElement{Name:xml.Name{Space:"", Local:"baz"}} xml.CharData{0xa, 0x9, 0x9} xml.StartElement{Name:xml.Name{Space:"", Local:"baz"}, Attr:[]xml.Attr{}} xml.CharData{0x32, 0x33} xml.EndElement{Name:xml.Name{Space:"", Local:"baz"}} xml.CharData{0xa, 0x9, 0x9} xml.StartElement{Name:xml.Name{Space:"", Local:"baz"}, Attr:[]xml.Attr{}} xml.CharData{0x79, 0x61, 0x79} xml.EndElement{Name:xml.Name{Space:"", Local:"baz"}} xml.CharData{0xa, 0x9, 0x9} xml.StartElement{Name:xml.Name{Space:"", Local:"baz"}, Attr:[]xml.Attr{}} xml.CharData{0x34, 0x39} stored err: sample