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