Documentation
¶
Overview ¶
Package seqreader is a utility library for representing fallible sequences.
A fallible sequence is a series of values for which an error could potentially occur on any read.
SeqReader is therefore a fallible equivalent of iter.Seq, whose design is inspired by io.Reader. iter.Seq and iter.Seq2 are both designed for infallible sequences and the common patterns using them are not well-suited to fallible sequences.
This package also includes various adapter functions to help with using SeqReader values where iter.Seq or iter.Seq2 values are expected, using iter.Seq values where SeqReader values are expected, and using io.Reader values where SeqReader values are expected.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrEndOfSeq = errors.New("end of sequence")
ErrEndOfSeq is the error returned by [SeqReader.ReadSeq] once the end of a finite sequence has been reached.
Functions ¶
func ToSeq2 ¶
ToSeq2 returns an iter.Seq2 over results from the given SeqReader, where each item in the sequence includes both a result and an error.
Use this to adapt a SeqReader to a situation that requires an iter.Seq2. For example:
for item, err := range seqreader.ToSeq2[seqReader] {
if err != nil {
// handle err
}
// handle item
}
Example ¶
// This is a SeqReader[string] that fails on reads three and onward.
r := SeqReaderThatFails()
for msg, err := range seqreader.ToSeq2(r) {
if err != nil {
fmt.Printf("error: %s\n", err)
break // this sequence never stops returning errors, so must break
}
fmt.Printf("msg = %q\n", msg)
}
Output: msg = "hello" msg = "world" error: failed to ding the wotsit
Types ¶
type FallibleSeq ¶
type FallibleSeq[T any] struct { // contains filtered or unexported fields }
FallibleSeq provides an iter.Seq over items of type T where reads can potentially fail with an error.
After exhausting the sequence, call method Err to determine whether an error occured:
for item := range fallibleSeq.Items() {
// Do something with item
}
if err := fallibleSeq.Err(); err != nil {
// Handle err
}
func ToSeq ¶
func ToSeq[T any](r SeqReader[T]) *FallibleSeq[T]
ToSeq adapts the given SeqReader into an object that provides both an iter.Seq and a possible error encountered while reading from it.
This is to allow using a SeqReader with a function that expects iter.Seq, by splitting the iteration responsibility from the error-handling responsibility.
Example ¶
// This is a SeqReader[string] that fails on its third read.
r := SeqReaderThatFails()
messages := seqreader.ToSeq(r)
// Use the result of the Items method with something that expects
// an infallible, finite iter.Seq.
slice := slices.Collect(messages.Items())
// After that operation is complete, call Err to find out if
// the traversal ended early due to an error.
if err := messages.Err(); err != nil {
fmt.Printf("err = %q\n", err)
}
fmt.Printf("slice = %#v\n", slice)
Output: err = "failed to ding the wotsit" slice = []string{"hello", "world"}
func (*FallibleSeq[T]) Err ¶
func (s *FallibleSeq[T]) Err() error
func (*FallibleSeq[T]) Items ¶
func (s *FallibleSeq[T]) Items() iter.Seq[T]
Items returns an iter.Seq over the items in the sequence, which terminates either when the underlying sequence is exhausted or when reading from that sequence returns an error.
Call method Err after the sequence is exhausted to determine whether an error occurred.
type SeqReadCloser ¶
SeqReadCloser is a SeqReader that also implements io.Close, which the caller should call once they are finished reading items from the sequence.
func FromSeq ¶
func FromSeq[T any](seq iter.Seq[T]) SeqReadCloser[T]
FromSeq adapts the given seq into a SeqReadCloser.
Calling Close on the result tells the sequence to terminate.
Example ¶
package main
import (
"fmt"
"os"
"slices"
"github.com/apparentlymart/go-seqreader/seqreader"
)
func main() {
seq := slices.Values([]string{"hello", "world"})
r := seqreader.FromSeq(seq)
defer r.Close()
for {
s, err := r.ReadSeq()
if err == seqreader.ErrEndOfSeq {
break
}
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to read: %s\n", err)
break
}
fmt.Println(s)
}
}
Output: hello world
type SeqReader ¶
type SeqReader[T any] interface { // ReadSeq either returns the next item in the sequence or returns an // error describing why another item cannot be read. // // At the end of the sequence the error is [ErrEndOfSeq]. ReadSeq() (T, error) }
SeqReader is a sequence of items of type T from which reads can potentially fail.
for {
item, err := seqReader.ReadSeq()
if err == ErrEndOfSeq {
break
}
if err != nil {
// handle err
}
// handle item
}
func FromIOReader ¶
FromIOReader returns a SeqReader over byte slices returned from the given reader.
The resulting reader needs byte arrays to recieve data from the io.Reader. makeBuf should return a slice whose length is the maximum size of a single read but which may have excess capacity to allow using the remainder of the underlying array for future reads as long as the remaining capacity is at least the original slice length.
The SeqReader returns ErrEndOfSeq when the underlying io.Reader returns io.EOF.
The result dynamically implmenets SeqReadCloser, calling Close on the given reader if it is actually an io.ReadCloser, or a no-op success if not.
Example ¶
package main
import (
"bytes"
"fmt"
"os"
"github.com/apparentlymart/go-seqreader/seqreader"
)
func main() {
buf := []byte(`hello, world of fallible sequences!`)
ioR := bytes.NewReader(buf)
r := seqreader.FromIOReader(ioR, func() []byte {
fmt.Println("(allocating buffer)")
return make([]byte, 4, 16)
})
for {
chunk, err := r.ReadSeq()
if err == seqreader.ErrEndOfSeq {
break
}
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to read: %s\n", err)
break
}
fmt.Printf("chunk: %q\n", chunk)
}
}
Output: (allocating buffer) chunk: "hell" chunk: "o, w" chunk: "orld" chunk: " of " (allocating buffer) chunk: "fall" chunk: "ible" chunk: " seq" chunk: "uenc" (allocating buffer) chunk: "es!"
func ItemsFromSliceReader ¶
ItemsFromSliceReader adapts a SeqReader over slices into a SeqReader over individual items from those slices, as if all concatenated together.
For example, this can adapt a SeqReader[[]byte] into a SeqReader[byte], making the sequence of byte slices appear as a flat sequence of bytes.
The given sequence must either have a finite number of items or must eventually produce a non-empty item, or reading from the resulting sequence will fail to terminate.
Example ¶
package main
import (
"fmt"
"os"
"slices"
"github.com/apparentlymart/go-seqreader/seqreader"
)
func main() {
seq := slices.Values([][]byte{[]byte("hello "), []byte("world")})
sliceR := seqreader.FromSeq(seq)
r := seqreader.ItemsFromSliceReader(sliceR)
for {
b, err := r.ReadSeq()
if err == seqreader.ErrEndOfSeq {
break
}
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to read: %s\n", err)
break
}
fmt.Printf("byte: 0x%02x (%c)\n", b, b)
}
}
Output: byte: 0x68 (h) byte: 0x65 (e) byte: 0x6c (l) byte: 0x6c (l) byte: 0x6f (o) byte: 0x20 ( ) byte: 0x77 (w) byte: 0x6f (o) byte: 0x72 (r) byte: 0x6c (l) byte: 0x64 (d)