From 71ee6f3d04c0c8dbc01ca31d916d4e5dc46ce3fd Mon Sep 17 00:00:00 2001 From: milarin Date: Fri, 30 Jun 2023 20:43:23 +0200 Subject: [PATCH] initial commit --- errors.go | 10 +++++ go.mod | 8 ++++ go.sum | 4 ++ reader.go | 119 +++++++++++++++++++++++++++++++++++++++++++++++++ reader_test.go | 13 ++++++ runefunc.go | 38 ++++++++++++++++ utils.go | 33 ++++++++++++++ 7 files changed, 225 insertions(+) create mode 100644 errors.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 reader.go create mode 100644 reader_test.go create mode 100644 runefunc.go create mode 100644 utils.go diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..a5a653a --- /dev/null +++ b/errors.go @@ -0,0 +1,10 @@ +package anyreader + +import ( + "git.milar.in/milarin/adverr" +) + +var ( + ErrNothingToUnread = adverr.NewErrTmpl("ErrNothingToUnread", "Unreading failed because there wasn't any Read yet") + ErrNoMoreValues = adverr.NewErrTmpl("ErrNoMoreValues", "source does not have any more values") +) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e591a7f --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module git.milar.in/milarin/anyreader + +go 1.20 + +require ( + git.milar.in/milarin/adverr v1.1.0 + git.milar.in/milarin/ds v0.0.2 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bb49ff3 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +git.milar.in/milarin/adverr v1.1.0 h1:jD9WnOvs40lfMhvqQ7cllOaRJNBMWr1f07/s9jAadp0= +git.milar.in/milarin/adverr v1.1.0/go.mod h1:joU9sBb7ySyNv4SpTXB0Z4o1mjXsArBw4N27wjgzj9E= +git.milar.in/milarin/ds v0.0.2 h1:vCA3mDxZUNfvHpzrdz7SeBUKiPn74NTopo915IUG7I0= +git.milar.in/milarin/ds v0.0.2/go.mod h1:HJK7QERcRvV9j7xzEocrKUtW+1q4JB1Ly4Bj54chfwI= diff --git a/reader.go b/reader.go new file mode 100644 index 0000000..85888e8 --- /dev/null +++ b/reader.go @@ -0,0 +1,119 @@ +package anyreader + +import "git.milar.in/milarin/ds" + +type Reader[T any] struct { + buf ds.Stack[T] + src func() (T, error) +} + +func NewReaderFromSlice[T any](s []T) *Reader[T] { + return NewReaderFromErrorFunc(sliceToFunc(s)) +} + +func NewReaderFromFunc[T any](src func() T) *Reader[T] { + return NewReaderFromErrorFunc(func() (T, error) { return src(), nil }) +} + +func NewReaderFromErrorFunc[T any](src func() (T, error)) *Reader[T] { + return &Reader[T]{ + src: src, + buf: ds.NewArrayStack[T](), + } +} + +func (r *Reader[T]) Read() (T, error) { + v, err := r.src() + if err == nil { + r.buf.Push(v) + } + return v, err +} + +func (r *Reader[T]) Unread() error { + if r.buf.Empty() { + return ErrNothingToUnread.New() + } + + v := r.buf.Pop() + + returned := false + oldSrc := r.src + r.src = func() (T, error) { + if returned { + return oldSrc() + } + + returned = true + return v, nil + } + + return nil +} + +func (r *Reader[T]) UnreadN(n int) error { + for i := 0; i < n; i++ { + err := r.Unread() + if err != nil { + return err + } + } + return nil +} + +func (r *Reader[T]) Peek() (T, error) { + value, err := r.Read() + if err != nil { + return *new(T), err + } + + if err := r.Unread(); err != nil { + return *new(T), err + } + + return value, nil +} + +func (r *Reader[T]) ReadWhile(f ...func(T) bool) ([]T, error) { + res := make([]T, 0, 10) + + var value T + var err error + for value, err = r.Read(); err == nil && findFirstTrue(value, f); value, err = r.Read() { + res = append(res, value) + } + + return res, err +} + +func (r *Reader[T]) ReadUntil(f ...func(T) bool) ([]T, error) { + return r.ReadWhile(func(v T) bool { return !findFirstTrue(v, f) }) +} + +func (r *Reader[T]) SkipUntil(f ...func(T) bool) error { + _, err := r.ReadUntil(f...) + if err != nil { + return err + } + return r.Unread() +} + +func (r *Reader[T]) SkipWhile(f ...func(T) bool) error { + _, err := r.ReadWhile(f...) + if err != nil { + return err + } + return r.Unread() +} + +func (r *Reader[T]) Expect(f ...func(T) bool) (bool, error) { + value, err := r.Read() + if err != nil { + return false, err + } + return findFirstTrue(value, f), nil +} + +func (r *Reader[T]) Commit() { + r.buf.Clear() +} diff --git a/reader_test.go b/reader_test.go new file mode 100644 index 0000000..805a60f --- /dev/null +++ b/reader_test.go @@ -0,0 +1,13 @@ +package anyreader + +import ( + "fmt" + "testing" +) + +func TestReader(t *testing.T) { + r := NewReaderFromSlice([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + fmt.Println(r.Read()) + r.Unread() + fmt.Println(r.Read()) +} diff --git a/runefunc.go b/runefunc.go new file mode 100644 index 0000000..1b4cfe7 --- /dev/null +++ b/runefunc.go @@ -0,0 +1,38 @@ +package anyreader + +import "git.milar.in/milarin/ds" + +func And[T any](f ...func(T) bool) func(T) bool { + return func(value T) bool { + return findFirstFalse(value, f) + } +} + +func Or[T any](f ...func(T) bool) func(T) bool { + return func(value T) bool { + return findFirstTrue(value, f) + } +} + +func Not[T any](f func(T) bool) func(T) bool { + return func(value T) bool { + return !f(value) + } +} + +func Is[T comparable](value T) func(T) bool { + return func(v T) bool { + return value == v + } +} + +func OneOf[T comparable](values ...T) func(T) bool { + m := ds.NewSet[T]() + for _, value := range values { + m.Add(value) + } + + return func(v T) bool { + return m.Has(v) + } +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..22458a8 --- /dev/null +++ b/utils.go @@ -0,0 +1,33 @@ +package anyreader + +func sliceToFunc[T any](s []T) func() (T, error) { + i := 0 + return func() (T, error) { + c := i + + if c >= len(s) { + return *new(T), ErrNoMoreValues.New() + } + + i++ + return s[c], nil + } +} + +func findFirstTrue[T any](value T, functions []func(T) bool) bool { + for _, f := range functions { + if f(value) { + return true + } + } + return false +} + +func findFirstFalse[T any](rn T, functions []func(T) bool) bool { + for _, f := range functions { + if !f(rn) { + return false + } + } + return true +}