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 values over 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.
Caveats: Box remembers the first non-nil error but it DOES NOT mean that Box converts an input iterator to be stateful. In case users need Box.IntoIter to be break-ed and resumed, users must ensure that the input is stateful, or is ok to replay it. To convert iterators to be stateful, github.com/ngicks/go-iterator-helper/hiter/iterable.NewResumable is useful.
*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 paired to the error. *Box.Err returns that error otherwise nil. After the first non-nil error, *Box.IntoIter return an iterator that yields nothing.
The zero Box is invalid and it must be allocated by New.
func Map ¶ added in v0.0.19
Map maps values over the input iterator and put them in Box.
See doc comment for Box for detailed caveats.
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.
See doc comment for Box for detailed caveats.
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 (partially) stateful; If the iterator produce a non-nil error, it stops iteration without yielding paired value(V) and will no longer produce any data. In that case the error can be inspected by calling *Box.Err.
type Box2 ¶ added in v0.0.19
type Box2[K, V any] struct { // contains filtered or unexported fields }
Box2 is like Box but with iter.Seq2.
See doc comment for Box for detail.
func Map2 ¶ added in v0.0.19
func Map2[K1, V1, K2, V2 any](mapper func(K1, V1) (K2, V2, error), seq iter.Seq2[K1, V1]) *Box2[K2, V2]
Map2 maps pairs of values over the input iterator using mapper and put them in Box2.
See doc comment for Box for detailed caveats.
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))) 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), ), ), ) 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")), ), ), ) 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) 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) }, ) 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) 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)))) 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)), ), ), ) 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")), ), ), ) 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