mirror of https://github.com/0xERR0R/blocky.git
94 lines
2.3 KiB
Go
94 lines
2.3 KiB
Go
package parsers
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// SeriesParser parses a series of `T`.
|
|
type SeriesParser[T any] interface {
|
|
// Next advances the cursor in the underlying data source,
|
|
// and returns a `T`, or an error.
|
|
//
|
|
// Fatal parse errors, where no more calls to `Next` should
|
|
// be made are of type `NonResumableError`.
|
|
// Other errors apply to the item being parsed, and have no
|
|
// impact on the rest of the series.
|
|
Next(context.Context) (T, error)
|
|
|
|
// Position returns a string that gives an user readable indication
|
|
// as to where in the parser's underlying data source the cursor is.
|
|
//
|
|
// The string should be understandable easily by the user.
|
|
Position() string
|
|
}
|
|
|
|
// ForEach is a helper for consuming a parser.
|
|
//
|
|
// It stops iteration at the first error encountered.
|
|
// If that error is `io.EOF`, `nil` is returned instead.
|
|
// Any other error is wrapped with the parser's position using `ErrWithPosition`.
|
|
//
|
|
// To continue iteration on resumable errors, use with `FilterErrors`.
|
|
func ForEach[T any](ctx context.Context, parser SeriesParser[T], callback func(T) error) (rerr error) {
|
|
defer func() {
|
|
rerr = ErrWithPosition(parser, rerr)
|
|
}()
|
|
|
|
for {
|
|
if err := ctx.Err(); err != nil {
|
|
return err
|
|
}
|
|
|
|
res, err := parser.Next(ctx)
|
|
if err != nil {
|
|
if errors.Is(err, io.EOF) {
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
err = callback(res)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
// ErrWithPosition adds the `parser`'s position to the given `err`.
|
|
func ErrWithPosition[T any](parser SeriesParser[T], err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("%s: %w", parser.Position(), err)
|
|
}
|
|
|
|
// IsNonResumableErr is a helper to check if an error returned by a parser is resumable.
|
|
func IsNonResumableErr(err error) bool {
|
|
var nonResumableError *NonResumableError
|
|
|
|
return errors.As(err, &nonResumableError)
|
|
}
|
|
|
|
// NonResumableError represents an error from which a parser cannot recover.
|
|
type NonResumableError struct {
|
|
inner error
|
|
}
|
|
|
|
// NewNonResumableError creates and returns a new `NonResumableError`.
|
|
func NewNonResumableError(inner error) error {
|
|
return &NonResumableError{inner}
|
|
}
|
|
|
|
func (e *NonResumableError) Error() string {
|
|
return fmt.Sprintf("non resumable parse error: %s", e.inner.Error())
|
|
}
|
|
|
|
func (e *NonResumableError) Unwrap() error {
|
|
return e.inner
|
|
}
|