mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-10-10 17:24:02 +02:00
1044 lines
26 KiB
Go
1044 lines
26 KiB
Go
package g
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
|
|
"github.com/enetx/g/cmp"
|
|
"github.com/enetx/g/constraints"
|
|
)
|
|
|
|
// SeqResult is an iterator over sequences of Result[V] values.
|
|
type SeqResult[V any] func(yield func(Result[V]) bool)
|
|
|
|
// OkSeq wraps a single Ok value into a SeqResult iterator.
|
|
// Useful for returning a successful single-element sequence without manually
|
|
// constructing the yield function.
|
|
//
|
|
// Example:
|
|
//
|
|
// return OkSeq(42)
|
|
func OkSeq[V any](v V) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) { yield(Ok(v)) }
|
|
}
|
|
|
|
// ErrSeq wraps a single error into a SeqResult iterator.
|
|
// Useful for early returns in functions that produce a SeqResult,
|
|
// where a plain error needs to be lifted into the sequence type.
|
|
//
|
|
// Example:
|
|
//
|
|
// return ErrSeq[int](errors.New("something went wrong"))
|
|
func ErrSeq[V any](err error) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) { yield(Err[V](err)) }
|
|
}
|
|
|
|
// FlatMap transforms each Ok value into a sequence and flattens the results,
|
|
// wrapping each produced element in Ok. The element type may differ from the
|
|
// input type. If an Err is encountered, it is passed downstream as-is;
|
|
// iteration continues for as long as the consumer keeps accepting values
|
|
// (consumer-driven), matching Map.
|
|
//
|
|
// Example:
|
|
//
|
|
// seq.FlatMap(Slice[Int].Iter) // SeqResult[Slice[Int]] -> SeqResult[Int]
|
|
func (seq SeqResult[V]) FlatMap[U any](fn func(V) Seq[U]) SeqResult[U] {
|
|
return func(yield func(Result[U]) bool) {
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(Err[U](v.err))
|
|
}
|
|
|
|
cont := true
|
|
fn(v.v)(func(u U) bool {
|
|
if !yield(Ok(u)) {
|
|
cont = false
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
|
|
return cont
|
|
})
|
|
}
|
|
}
|
|
|
|
// Pull converts the “push-style” sequence of Result[V] into a “pull-style” iterator accessed by two functions: next and stop.
|
|
//
|
|
// The next function returns the next Result[V] in the sequence and a boolean indicating whether the value is valid.
|
|
// When the sequence is over, next returns the zero value and false. It is valid to call next after reaching the end
|
|
// of the sequence or after calling stop. These calls will continue to return the zero value and false.
|
|
//
|
|
// The stop function ends the iteration. It must be called when the caller is no longer interested in next values and
|
|
// next has not yet signaled that the sequence is over. It is valid to call stop multiple times and after next has
|
|
// already returned false.
|
|
//
|
|
// It is an error to call next or stop from multiple goroutines simultaneously.
|
|
func (seq SeqResult[V]) Pull() (func() (Result[V], bool), func()) {
|
|
return Seq[Result[V]](seq).seqPull()
|
|
}
|
|
|
|
// All checks whether all Ok values in the sequence satisfy the provided condition.
|
|
//
|
|
// If an Err is encountered in the sequence, that Err is immediately returned.
|
|
// Otherwise, it returns Ok(true) if all Ok values satisfy the function, or Ok(false) if at least one does not.
|
|
func (seq SeqResult[V]) All(fn func(v V) bool) Result[bool] {
|
|
result := Ok(true)
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
result = Err[bool](v.err)
|
|
return false
|
|
}
|
|
|
|
if !fn(v.v) {
|
|
result = Ok(false)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
// Any checks whether any Ok value in the sequence satisfies the provided condition.
|
|
//
|
|
// If an Err is encountered, that Err is immediately returned.
|
|
// Otherwise, it returns Ok(true) if at least one Ok value satisfies the function, or Ok(false) if none do.
|
|
func (seq SeqResult[V]) Any(fn func(v V) bool) Result[bool] {
|
|
result := Ok(false)
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
result = Err[bool](v.err)
|
|
return false
|
|
}
|
|
|
|
if fn(v.v) {
|
|
result = Ok(true)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
// Collect returns a collector over the raw Result elements, matching the
|
|
// Seq/Seq2 collector idiom: materialize with .Slice() (etc.). Collect itself
|
|
// is lazy and does not consume the sequence; the materializer does. Both Ok
|
|
// and Err elements flow through as-is; encountering an Err does not stop
|
|
// collection. Use TryCollect for the short-circuiting Ok-only variant.
|
|
func (seq SeqResult[V]) Collect() collector[Result[V]] {
|
|
return collector[Result[V]]{Seq[Result[V]](seq)}
|
|
}
|
|
|
|
// TryCollect gathers the Ok values from the sequence into a Slice: the first Err
|
|
// short-circuits — iteration stops immediately, elements after it are not
|
|
// consumed — and that error is returned as Err. An empty sequence yields Ok of
|
|
// an empty Slice.
|
|
//
|
|
// Unlike Collect, which gathers every element (Ok and Err alike) into a
|
|
// Slice[Result[V]], TryCollect returns Result[Slice[V]]: either all the
|
|
// unwrapped Ok values, or the first error encountered.
|
|
func (seq SeqResult[V]) TryCollect() Result[Slice[V]] {
|
|
collection := NewSlice[V]()
|
|
|
|
var err error
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
err = v.err
|
|
return false
|
|
}
|
|
|
|
collection = append(collection, v.v)
|
|
|
|
return true
|
|
})
|
|
|
|
if err != nil {
|
|
return Err[Slice[V]](err)
|
|
}
|
|
|
|
return Ok(collection)
|
|
}
|
|
|
|
// Count consumes the entire sequence, counting the number of elements it yields.
|
|
// Err elements are counted like Ok elements and do not stop the count.
|
|
func (seq SeqResult[V]) Count() Int {
|
|
var counter Int
|
|
seq(func(Result[V]) bool {
|
|
counter++
|
|
return true
|
|
})
|
|
|
|
return counter
|
|
}
|
|
|
|
// Map transforms each Ok value in the sequence using the given function, returning a new sequence of Result.
|
|
// The result type may differ from the input type.
|
|
//
|
|
// If an Err is encountered, it is passed downstream as-is; iteration continues
|
|
// for as long as the consumer keeps accepting values (consumer-driven).
|
|
func (seq SeqResult[V]) Map[U any](transform func(V) U) SeqResult[U] {
|
|
return func(yield func(Result[U]) bool) {
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(Err[U](v.err))
|
|
}
|
|
return yield(Ok(transform(v.v)))
|
|
})
|
|
}
|
|
}
|
|
|
|
// Filter returns a new sequence containing only the Ok elements that satisfy the provided function.
|
|
//
|
|
// If an Err is encountered, it is yielded downstream as-is; the consumer decides
|
|
// whether to continue (consumer-driven).
|
|
// Only Ok elements for which fn returns true are yielded downstream as Ok.
|
|
func (seq SeqResult[V]) Filter(fn func(V) bool) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(v)
|
|
}
|
|
if fn(v.v) {
|
|
return yield(v)
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
}
|
|
|
|
// Exclude returns a new sequence that excludes Ok elements which satisfy the provided function.
|
|
//
|
|
// If an Err is encountered, it is yielded downstream as-is (consumer-driven).
|
|
// Only Ok elements for which 'fn' returns false are yielded downstream.
|
|
func (seq SeqResult[V]) Exclude(fn func(V) bool) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(v)
|
|
}
|
|
if !fn(v.v) {
|
|
return yield(v)
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
}
|
|
|
|
// Dedup removes consecutive duplicates of Ok values from the sequence, returning a new sequence.
|
|
//
|
|
// If an Err is encountered, it is yielded downstream as-is (consumer-driven).
|
|
// Consecutive Ok duplicates (based on equality) are filtered out so only the first occurrence is yielded.
|
|
func (seq SeqResult[V]) Dedup() SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
var current V
|
|
hasFirst := false
|
|
comparable := isValueComparable[V]()
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(v)
|
|
}
|
|
|
|
if !hasFirst {
|
|
hasFirst = true
|
|
current = v.v
|
|
return yield(v)
|
|
}
|
|
|
|
if comparable {
|
|
if any(current) == any(v.v) {
|
|
return true
|
|
}
|
|
} else {
|
|
if reflect.DeepEqual(current, v.v) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
current = v.v
|
|
return yield(v)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Unique returns a new sequence that contains only the first occurrence of each distinct Ok value.
|
|
//
|
|
// If an Err is encountered, it is yielded downstream as-is (consumer-driven).
|
|
// Future occurrences of a previously seen Ok value are skipped.
|
|
func (seq SeqResult[V]) Unique() SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
if isValueComparable[V]() {
|
|
seen := NewSet[any]()
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(v)
|
|
}
|
|
|
|
k := any(v.v)
|
|
if _, ok := seen[k]; !ok {
|
|
seen[k] = Unit{}
|
|
return yield(v)
|
|
}
|
|
|
|
return true
|
|
})
|
|
} else {
|
|
var seen Slice[V]
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(v)
|
|
}
|
|
|
|
for _, s := range seen {
|
|
if reflect.DeepEqual(s, v.v) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
seen = append(seen, v.v)
|
|
return yield(v)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// ForEach applies a function to each Result in the sequence (Ok or Err) without modifying the sequence.
|
|
//
|
|
// The iteration continues over all elements, passing them to fn for side effects.
|
|
func (seq SeqResult[V]) ForEach(fn func(v Result[V])) {
|
|
seq(func(v Result[V]) bool {
|
|
fn(v)
|
|
return true
|
|
})
|
|
}
|
|
|
|
// Range iterates through elements until the given function returns false.
|
|
//
|
|
// For each element (Ok or Err), fn is called. If fn returns false, iteration stops immediately.
|
|
func (seq SeqResult[V]) Range(fn func(v Result[V]) bool) {
|
|
seq(func(v Result[V]) bool {
|
|
return fn(v)
|
|
})
|
|
}
|
|
|
|
// Skip returns a new sequence that skips the first n Ok elements.
|
|
//
|
|
// If an Err is encountered, it is yielded as-is without consuming the skip
|
|
// budget (consumer-driven). Once n Ok elements have been skipped,
|
|
// subsequent elements (Ok or Err) are yielded normally.
|
|
func (seq SeqResult[V]) Skip(n Int) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
if n < 0 {
|
|
n = 0
|
|
}
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(v)
|
|
}
|
|
if n > 0 {
|
|
n--
|
|
return true
|
|
}
|
|
return yield(v)
|
|
})
|
|
}
|
|
}
|
|
|
|
// StepBy creates a new sequence that yields every nth Ok element from the original sequence.
|
|
//
|
|
// If an Err is encountered, it is yielded downstream as-is (consumer-driven).
|
|
// For Ok elements, only every n-th element is yielded.
|
|
func (seq SeqResult[V]) StepBy(n Int) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
if n <= 0 {
|
|
return
|
|
}
|
|
|
|
i := Int(0)
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(v)
|
|
}
|
|
i++
|
|
if (i-1)%n == 0 {
|
|
return yield(v)
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
}
|
|
|
|
// Take returns a new sequence with the first n Ok elements.
|
|
// If an Err is encountered, it is yielded downstream as-is (consumer-driven).
|
|
// After n Ok elements are yielded, the sequence ends.
|
|
func (seq SeqResult[V]) Take(n Int) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
if n <= 0 {
|
|
return
|
|
}
|
|
|
|
seq(func(v Result[V]) bool {
|
|
// Once n Ok values are taken, stop hard — nothing further (Ok or Err)
|
|
// is yielded, even if the source ignores our stop signal.
|
|
if n == 0 {
|
|
return false
|
|
}
|
|
|
|
if v.IsErr() {
|
|
return yield(v)
|
|
}
|
|
|
|
if !yield(v) {
|
|
return false
|
|
}
|
|
|
|
n--
|
|
|
|
// Stop tightly once n elements are taken so a well-behaved source is
|
|
// not pulled one extra time.
|
|
return n > 0
|
|
})
|
|
}
|
|
}
|
|
|
|
// Nth returns the nth Ok element (0-indexed) in the sequence.
|
|
// If an Err is encountered before reaching the nth element, that Err is returned.
|
|
// If there are fewer than n+1 Ok elements, None is returned.
|
|
func (seq SeqResult[V]) Nth(n Int) Result[Option[V]] {
|
|
if n < 0 {
|
|
return Ok(None[V]())
|
|
}
|
|
|
|
var i Int
|
|
result := Ok(None[V]())
|
|
found := false
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if found {
|
|
return false
|
|
}
|
|
if v.IsErr() {
|
|
result = Err[Option[V]](v.err)
|
|
found = true
|
|
return false
|
|
}
|
|
if i == n {
|
|
result = Ok(Some(v.v))
|
|
found = true
|
|
return false
|
|
}
|
|
i++
|
|
return true
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
// Chain concatenates this sequence with other sequences, returning a new sequence of Result[V].
|
|
//
|
|
// The function yields all elements (Ok or Err) from the current sequence, then from each of the provided sequences in order.
|
|
// Err elements are yielded like any other element (consumer-driven).
|
|
func (seq SeqResult[V]) Chain(seqs ...SeqResult[V]) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
stopped := false
|
|
|
|
for _, seq := range append([]SeqResult[V]{seq}, seqs...) {
|
|
seq(func(v Result[V]) bool {
|
|
if !yield(v) {
|
|
stopped = true
|
|
return false
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
if stopped {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Intersperse inserts the provided Ok separator between each Ok element of the sequence.
|
|
//
|
|
// If an Err is encountered, it is yielded as-is without a separator (consumer-driven).
|
|
// For Ok elements, after the first yield, a separator is inserted before each subsequent Ok value.
|
|
func (seq SeqResult[V]) Intersperse(sep V) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
first := true
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(v)
|
|
}
|
|
|
|
if !first && !yield(Ok(sep)) {
|
|
return false
|
|
}
|
|
|
|
first = false
|
|
return yield(v)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Inspect calls fn for every Ok value without changing it.
|
|
// Err elements are passed through unchanged (consumer-driven).
|
|
func (seq SeqResult[V]) Inspect(fn func(v V)) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(v)
|
|
}
|
|
fn(v.v)
|
|
return yield(v)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Find searches the sequence for the first Ok value that satisfies the provided function.
|
|
//
|
|
// If an Err is encountered, it returns that Err immediately. If a matching Ok value is found,
|
|
// iteration stops and we return Ok(Some(...)). If no matching Ok value is found, it returns Ok(None).
|
|
func (seq SeqResult[V]) Find(fn func(V) bool) Result[Option[V]] {
|
|
result := Ok(None[V]())
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
result = Err[Option[V]](v.err)
|
|
return false
|
|
}
|
|
if fn(v.v) {
|
|
result = Ok(Some(v.v))
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
// Context allows the iteration to be controlled with a context.Context.
|
|
func (seq SeqResult[V]) Context(ctx context.Context) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
seq(func(v Result[V]) bool {
|
|
select {
|
|
case <-ctx.Done():
|
|
return false
|
|
default:
|
|
return yield(v)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// First returns the first Ok element from the sequence.
|
|
// If the sequence is empty or contains only Err values, None is returned.
|
|
// If an Err is encountered, that Err is returned.
|
|
func (seq SeqResult[V]) First() Result[Option[V]] {
|
|
result := Ok(None[V]())
|
|
found := false
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if found {
|
|
return false
|
|
}
|
|
if v.IsErr() {
|
|
result = Err[Option[V]](v.err)
|
|
found = true
|
|
return false
|
|
}
|
|
result = Ok(Some(v.v))
|
|
found = true
|
|
return false
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
// Last returns the last Ok element from the sequence.
|
|
// If the sequence is empty or contains only Err values, None is returned.
|
|
// If an Err is encountered, that Err is returned.
|
|
func (seq SeqResult[V]) Last() Result[Option[V]] {
|
|
result := Ok(None[V]())
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
result = Err[Option[V]](v.err)
|
|
return false
|
|
}
|
|
result = Ok(Some(v.v))
|
|
return true
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
// Next extracts the next element from the iterator and advances it.
|
|
//
|
|
// This method consumes the next element from the iterator and returns it wrapped in an Option.
|
|
// The iterator itself is modified to point to the remaining elements.
|
|
//
|
|
// Returns:
|
|
// - Option[Result[V]]: Some(Result[V]) if an element exists, None if the iterator is exhausted.
|
|
func (seq *SeqResult[V]) Next() Option[Result[V]] {
|
|
if value, remaining, ok := Seq[Result[V]](*seq).seqNext(); ok {
|
|
*seq = SeqResult[V](remaining)
|
|
return Some(value)
|
|
}
|
|
|
|
return None[Result[V]]()
|
|
}
|
|
|
|
// Partition separates the sequence into two slices: one containing all Ok values and one containing all errors.
|
|
// The iteration continues through all elements, collecting each into the appropriate slice.
|
|
func (seq SeqResult[V]) Partition() (Slice[V], Slice[error]) {
|
|
ok := NewSlice[V]()
|
|
err := NewSlice[error]()
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsOk() {
|
|
ok = append(ok, v.v)
|
|
} else {
|
|
err = append(err, v.err)
|
|
}
|
|
return true
|
|
})
|
|
|
|
return ok, err
|
|
}
|
|
|
|
// Ok returns a new sequence containing only the Ok values from the original sequence.
|
|
// All Err values are filtered out.
|
|
func (seq SeqResult[V]) Ok() Seq[V] {
|
|
return Seq[V](func(yield func(V) bool) {
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsOk() {
|
|
return yield(v.v)
|
|
}
|
|
return true
|
|
})
|
|
})
|
|
}
|
|
|
|
// Err returns a new sequence containing only the error values from the original sequence.
|
|
// All Ok values are filtered out.
|
|
func (seq SeqResult[V]) Err() Seq[error] {
|
|
return Seq[error](func(yield func(error) bool) {
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(v.err)
|
|
}
|
|
return true
|
|
})
|
|
})
|
|
}
|
|
|
|
// FirstErr returns the first error encountered in the sequence.
|
|
// If no error is found, it returns None. The iteration stops at the first error.
|
|
func (seq SeqResult[V]) FirstErr() Option[error] {
|
|
result := None[error]()
|
|
found := false
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if found {
|
|
return false
|
|
}
|
|
if v.IsErr() {
|
|
result = Some(v.err)
|
|
found = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
// FromResultChan converts a channel of Results into a SeqResult iterator.
|
|
// It consumes the channel until it's closed, yielding each Result to the iterator.
|
|
// This is particularly useful with pool.Stream() for processing task results
|
|
// as they complete in real-time.
|
|
//
|
|
// Example usage with pool.Stream:
|
|
//
|
|
// p := pool.New[int]().Limit(10)
|
|
// ch := p.Stream(func() {
|
|
// for i := range 100 {
|
|
// p.Go(func() Result[int] {
|
|
// if i%10 == 0 {
|
|
// return Err[int](fmt.Errorf("task %d failed", i))
|
|
// }
|
|
// return Ok(i * i)
|
|
// })
|
|
// }
|
|
// })
|
|
//
|
|
// successful, failed := FromResultChan(ch).Partition()
|
|
// fmt.Printf("Successful: %d, Failed: %d\n", successful.Len(), failed.Len())
|
|
func FromResultChan[V any](ch <-chan Result[V]) SeqResult[V] {
|
|
return SeqResult[V](seqFromChan(ch))
|
|
}
|
|
|
|
// Fold reduces the sequence to a single value using an accumulator.
|
|
// The accumulator type may differ from the element type. The first Err
|
|
// short-circuits the iteration and is returned as Err.
|
|
func (seq SeqResult[V]) Fold[A any](init A, fn func(acc A, val V) A) Result[A] {
|
|
acc := init
|
|
var err error
|
|
|
|
seq(func(r Result[V]) bool {
|
|
if r.IsErr() {
|
|
err = r.err
|
|
return false
|
|
}
|
|
|
|
acc = fn(acc, r.v)
|
|
|
|
return true
|
|
})
|
|
|
|
if err != nil {
|
|
return Err[A](err)
|
|
}
|
|
|
|
return Ok(acc)
|
|
}
|
|
|
|
// SumBy maps each Ok value to a numeric value via fn and returns their sum wrapped in Ok.
|
|
// The first Err short-circuits: iteration stops and that error is returned as Err[S].
|
|
// An empty (or all-consumed) sequence yields Ok of the zero value of S.
|
|
func (seq SeqResult[V]) SumBy[S constraints.Number](fn func(V) S) Result[S] {
|
|
var zero S
|
|
return seq.Fold(zero, func(acc S, v V) S { return acc + fn(v) })
|
|
}
|
|
|
|
// ProductBy maps each Ok value to a numeric value via fn and returns their product
|
|
// wrapped in Ok. The first Err short-circuits and is returned as Err[S]. An empty
|
|
// (or all-consumed) sequence yields Ok of the multiplicative identity, one.
|
|
func (seq SeqResult[V]) ProductBy[S constraints.Number](fn func(V) S) Result[S] {
|
|
return seq.Fold(S(1), func(acc S, v V) S { return acc * fn(v) })
|
|
}
|
|
|
|
// FindMap applies fn to each Ok value and returns the first Some result wrapped in
|
|
// Ok; None if fn returns None for every value. The first Err short-circuits and is
|
|
// returned as Err.
|
|
func (seq SeqResult[V]) FindMap[U any](fn func(V) Option[U]) Result[Option[U]] {
|
|
result := Ok(None[U]())
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
result = Err[Option[U]](v.err)
|
|
return false
|
|
}
|
|
|
|
if o := fn(v.v); o.IsSome() {
|
|
result = Ok(o)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
// Reduce aggregates Ok values using the provided function:
|
|
// the first Err short-circuits and is returned as Err; an empty sequence yields Ok(None);
|
|
// otherwise Ok(Some(accumulated)).
|
|
func (seq SeqResult[V]) Reduce(fn func(a, b V) V) Result[Option[V]] {
|
|
var (
|
|
acc V
|
|
first = true
|
|
err error
|
|
)
|
|
|
|
seq(func(r Result[V]) bool {
|
|
if r.IsErr() {
|
|
err = r.err
|
|
return false
|
|
}
|
|
|
|
if first {
|
|
acc, first = r.v, false
|
|
} else {
|
|
acc = fn(acc, r.v)
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
if err != nil {
|
|
return Err[Option[V]](err)
|
|
}
|
|
|
|
return Ok(OptionOf(acc, !first))
|
|
}
|
|
|
|
// Scan accumulates Ok values, yielding the initial value followed by every
|
|
// intermediate accumulator state. The accumulator type may differ from the
|
|
// element type. An Err is passed downstream as-is without touching the
|
|
// accumulator; iteration continues for as long as the consumer keeps
|
|
// accepting values (consumer-driven).
|
|
func (seq SeqResult[V]) Scan[A any](init A, fn func(acc A, val V) A) SeqResult[A] {
|
|
return func(yield func(Result[A]) bool) {
|
|
if !yield(Ok(init)) {
|
|
return
|
|
}
|
|
|
|
acc := init
|
|
|
|
seq(func(r Result[V]) bool {
|
|
if r.IsErr() {
|
|
return yield(Err[A](r.err))
|
|
}
|
|
|
|
acc = fn(acc, r.v)
|
|
|
|
return yield(Ok(acc))
|
|
})
|
|
}
|
|
}
|
|
|
|
// FilterMap transforms each Ok value with fn and keeps only the Some results,
|
|
// changing the element type from V to U.
|
|
//
|
|
// If an Err is encountered, it is passed downstream as-is (Err[U]); the consumer
|
|
// decides whether to continue (consumer-driven). For an Ok value, fn is applied:
|
|
// Some(u) is yielded as Ok(u), None drops the element.
|
|
func (seq SeqResult[V]) FilterMap[U any](fn func(V) Option[U]) SeqResult[U] {
|
|
return func(yield func(Result[U]) bool) {
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(Err[U](v.err))
|
|
}
|
|
|
|
if u, ok := fn(v.v).Option(); ok {
|
|
return yield(Ok(u))
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
}
|
|
|
|
// TakeWhile yields Ok values while fn returns true, stopping at the first Ok
|
|
// value for which fn returns false.
|
|
//
|
|
// If an Err is encountered, it is passed downstream as-is and does not stop the
|
|
// taking (consumer-driven); only a failing predicate on an Ok value ends it.
|
|
func (seq SeqResult[V]) TakeWhile(fn func(V) bool) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(v)
|
|
}
|
|
|
|
if !fn(v.v) {
|
|
return false
|
|
}
|
|
|
|
return yield(v)
|
|
})
|
|
}
|
|
}
|
|
|
|
// SkipWhile skips Ok values while fn returns true, then yields every remaining
|
|
// element.
|
|
//
|
|
// If an Err is encountered, it is passed downstream as-is regardless of the
|
|
// skipping phase (consumer-driven); the skipping predicate is evaluated only on
|
|
// Ok values.
|
|
func (seq SeqResult[V]) SkipWhile(fn func(V) bool) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
skipping := true
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(v)
|
|
}
|
|
|
|
if skipping && fn(v.v) {
|
|
return true
|
|
}
|
|
|
|
skipping = false
|
|
|
|
return yield(v)
|
|
})
|
|
}
|
|
}
|
|
|
|
// MaxBy returns the maximum Ok value according to fn, mirroring the short-circuit
|
|
// terminals: the first Err stops iteration and is returned as Err; a sequence with
|
|
// no Ok values yields Ok(None).
|
|
func (seq SeqResult[V]) MaxBy(fn func(V, V) cmp.Ordering) Result[Option[V]] {
|
|
var best V
|
|
has := false
|
|
|
|
result := Ok(None[V]())
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
result = Err[Option[V]](v.err)
|
|
return false
|
|
}
|
|
|
|
if !has || fn(best, v.v).IsLt() {
|
|
best = v.v
|
|
has = true
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
if result.IsErr() {
|
|
return result
|
|
}
|
|
|
|
if has {
|
|
return Ok(Some(best))
|
|
}
|
|
|
|
return Ok(None[V]())
|
|
}
|
|
|
|
// MinBy returns the minimum Ok value according to fn, mirroring the short-circuit
|
|
// terminals: the first Err stops iteration and is returned as Err; a sequence with
|
|
// no Ok values yields Ok(None).
|
|
func (seq SeqResult[V]) MinBy(fn func(V, V) cmp.Ordering) Result[Option[V]] {
|
|
var best V
|
|
has := false
|
|
|
|
result := Ok(None[V]())
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
result = Err[Option[V]](v.err)
|
|
return false
|
|
}
|
|
|
|
if !has || fn(v.v, best).IsLt() {
|
|
best = v.v
|
|
has = true
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
if result.IsErr() {
|
|
return result
|
|
}
|
|
|
|
if has {
|
|
return Ok(Some(best))
|
|
}
|
|
|
|
return Ok(None[V]())
|
|
}
|
|
|
|
// Flatten flattens one or more levels of nested slices/arrays inside each Ok
|
|
// value, yielding the leaf elements as Ok. Err elements are passed downstream
|
|
// as-is (consumer-driven). It mirrors Seq.Flatten and, like it, relies on
|
|
// reflection: only leaves assignable to V are yielded.
|
|
func (seq SeqResult[V]) Flatten() SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
emit := func(v V) bool { return yield(Ok(v)) }
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
return yield(v)
|
|
}
|
|
|
|
return flattenValue(v.v, emit)
|
|
})
|
|
}
|
|
}
|
|
|
|
// SortBy consumes the sequence, sorts the Ok values with fn, and re-emits them
|
|
// in order as a SeqResult. Being a sort, it is eager: the whole sequence is
|
|
// buffered first. The first Err short-circuits — buffering stops and only that
|
|
// Err is yielded downstream.
|
|
func (seq SeqResult[V]) SortBy(fn func(a, b V) cmp.Ordering) SeqResult[V] {
|
|
return func(yield func(Result[V]) bool) {
|
|
items := NewSlice[V]()
|
|
|
|
var err error
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
err = v.err
|
|
return false
|
|
}
|
|
|
|
items = append(items, v.v)
|
|
|
|
return true
|
|
})
|
|
|
|
if err != nil {
|
|
yield(Err[V](err))
|
|
return
|
|
}
|
|
|
|
items.SortBy(fn)
|
|
|
|
for _, v := range items {
|
|
if !yield(Ok(v)) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// CounterBy counts how many Ok values map to each key produced by fn, returning
|
|
// the tally as plain pairs in first-seen key order (convert with MapOrd[K, Int]
|
|
// if map access is needed). It is a short-circuit terminal: the first Err stops
|
|
// the count and is returned as Err. (A lazy SeqResult of the tally is
|
|
// impossible here — it would instantiate SeqResult with a type built from V and
|
|
// hit an instantiation cycle; returning MapOrd would weld SeqResult to the map
|
|
// cluster.)
|
|
func (seq SeqResult[V]) CounterBy[K comparable](fn func(V) K) Result[[]Pair[K, Int]] {
|
|
order := NewSlice[K]()
|
|
counts := NewMap[K, Int]()
|
|
|
|
var err error
|
|
|
|
seq(func(v Result[V]) bool {
|
|
if v.IsErr() {
|
|
err = v.err
|
|
return false
|
|
}
|
|
|
|
k := fn(v.v)
|
|
if !counts.Contains(k) {
|
|
order.Push(k)
|
|
}
|
|
|
|
counts[k]++
|
|
|
|
return true
|
|
})
|
|
|
|
if err != nil {
|
|
return Err[[]Pair[K, Int]](err)
|
|
}
|
|
|
|
result := make([]Pair[K, Int], 0, order.Len())
|
|
for _, k := range order {
|
|
result = append(result, Pair[K, Int]{Key: k, Value: counts[k]})
|
|
}
|
|
|
|
return Ok(result)
|
|
}
|