mirror of
https://github.com/logos-storage/nim-asynciterators.git
synced 2026-01-02 13:33:08 +00:00
213 lines
5.6 KiB
Nim
213 lines
5.6 KiB
Nim
## Nim-Codex
|
|
## Copyright (c) 2025 Status Research & Development GmbH
|
|
## Licensed under either of
|
|
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
|
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
|
## at your option.
|
|
## This file may not be copied, modified, or distributed except according to
|
|
## those terms.
|
|
|
|
{.push raises: [].}
|
|
|
|
import std/sugar
|
|
|
|
import pkg/questionable
|
|
import pkg/questionable/results
|
|
|
|
## Public interface:
|
|
##
|
|
## Attributes
|
|
## - next - allows to set a custom function to be called when the next item is requested
|
|
##
|
|
## Operations:
|
|
## - new - to create a new iterator (Iter)
|
|
## - finish - to finish the iterator
|
|
## - finished - to check if the iterator is finished
|
|
## - next - to get the next item from the iterator
|
|
## - items - to iterate over the iterator
|
|
## - pairs - to iterate over the iterator and return the index of each item
|
|
## - map - to convert one iterator (Iter) to another iterator (Iter)
|
|
## - mapFilter - to convert one iterator (Iter) to another iterator (Iter) and apply filtering at the same time
|
|
## - filter - to filter an iterator (Iter) and return another iterator (Iter)
|
|
## - empty - to create an empty async iterator (AsyncIter)
|
|
|
|
type
|
|
IterFunction[T, U] = proc(value: T): U {.raises: [CatchableError], gcsafe.}
|
|
IterIsFinished = proc(): bool {.raises: [], gcsafe.}
|
|
IterGenNext[T] = proc(): T {.raises: [CatchableError], gcsafe.}
|
|
Iterator[T] = iterator (): T
|
|
|
|
Iter*[T] = ref object
|
|
finished: bool
|
|
next*: IterGenNext[T]
|
|
|
|
########################################################################
|
|
## Iter public interface methods
|
|
########################################################################
|
|
|
|
proc new*[T](
|
|
_: type Iter[T],
|
|
genNext: IterGenNext[T],
|
|
isFinished: IterIsFinished,
|
|
finishOnErr: bool = true,
|
|
): Iter[T] =
|
|
## Creates a new Iter using elements returned by supplier function `genNext`.
|
|
## Iter is finished whenever `isFinished` returns true.
|
|
##
|
|
|
|
var iter = Iter[T]()
|
|
|
|
proc next(): T {.raises: [CatchableError].} =
|
|
if not iter.finished:
|
|
var item: T
|
|
try:
|
|
item = genNext()
|
|
except CatchableError as err:
|
|
if finishOnErr or isFinished():
|
|
iter.finish
|
|
raise err
|
|
|
|
if isFinished():
|
|
iter.finish
|
|
return item
|
|
else:
|
|
raise newException(CatchableError, "Iter is finished but next item was requested")
|
|
|
|
if isFinished():
|
|
iter.finish
|
|
|
|
iter.next = next
|
|
return iter
|
|
|
|
proc new*[U, V, S: Ordinal](_: type Iter[U], a: U, b: V, step: S = 1): Iter[U] =
|
|
## Creates a new Iter in range a..b with specified step (default 1)
|
|
##
|
|
|
|
var i = a
|
|
|
|
proc genNext(): U =
|
|
let u = i
|
|
inc(i, step)
|
|
u
|
|
|
|
proc isFinished(): bool =
|
|
(step > 0 and i > b) or (step < 0 and i < b)
|
|
|
|
Iter[U].new(genNext, isFinished)
|
|
|
|
proc new*[U, V: Ordinal](_: type Iter[U], slice: HSlice[U, V]): Iter[U] =
|
|
## Creates a new Iter from a slice
|
|
##
|
|
|
|
Iter[U].new(slice.a.int, slice.b.int, 1)
|
|
|
|
proc new*[T](_: type Iter[T], items: seq[T]): Iter[T] =
|
|
## Creates a new Iter from a sequence
|
|
##
|
|
|
|
Iter[int].new(0 ..< items.len).map((i: int) => items[i])
|
|
|
|
proc new*[T](_: type Iter[T], iter: Iterator[T]): Iter[T] =
|
|
## Creates a new Iter from an iterator
|
|
##
|
|
var nextOrErr: Option[?!T]
|
|
proc tryNext(): void =
|
|
nextOrErr = none(?!T)
|
|
while not iter.finished:
|
|
try:
|
|
let t: T = iter()
|
|
if not iter.finished:
|
|
nextOrErr = some(success(t))
|
|
break
|
|
except CatchableError as err:
|
|
nextOrErr = some(T.failure(err))
|
|
|
|
proc genNext(): T {.raises: [CatchableError].} =
|
|
if nextOrErr.isNone:
|
|
raise newException(CatchableError, "Iterator finished but genNext was called")
|
|
|
|
without u =? nextOrErr.unsafeGet, err:
|
|
raise err
|
|
|
|
tryNext()
|
|
return u
|
|
|
|
proc isFinished(): bool =
|
|
nextOrErr.isNone
|
|
|
|
tryNext()
|
|
Iter[T].new(genNext, isFinished)
|
|
|
|
proc finish*[T](self: Iter[T]): void =
|
|
self.finished = true
|
|
|
|
proc finished*[T](self: Iter[T]): bool =
|
|
self.finished
|
|
|
|
iterator items*[T](self: Iter[T]): T =
|
|
while not self.finished:
|
|
yield self.next()
|
|
|
|
iterator pairs*[T](self: Iter[T]): tuple[key: int, val: T] {.inline.} =
|
|
var i = 0
|
|
while not self.finished:
|
|
yield (i, self.next())
|
|
inc(i)
|
|
|
|
proc map*[T, U](iter: Iter[T], fn: IterFunction[T, U]): Iter[U] =
|
|
Iter[U].new(genNext = () => fn(iter.next()), isFinished = () => iter.finished)
|
|
|
|
proc mapFilter*[T, U](
|
|
iter: Iter[T], mapPredicate: IterFunction[T, Option[U]]
|
|
): Iter[U] =
|
|
var nextUOrErr: Option[?!U]
|
|
|
|
proc tryFetch(): void =
|
|
nextUOrErr = none(?!U)
|
|
while not iter.finished:
|
|
try:
|
|
let t = iter.next()
|
|
if u =? mapPredicate(t):
|
|
nextUOrErr = some(success(u))
|
|
break
|
|
except CatchableError as err:
|
|
nextUOrErr = some(U.failure(err))
|
|
|
|
proc genNext(): U {.raises: [CatchableError].} =
|
|
if nextUOrErr.isNone:
|
|
raise newException(CatchableError, "Iterator finished but genNext was called")
|
|
|
|
# at this point nextUOrErr should always be some(..)
|
|
without u =? nextUOrErr.unsafeGet, err:
|
|
raise err
|
|
|
|
tryFetch()
|
|
return u
|
|
|
|
proc isFinished(): bool =
|
|
nextUOrErr.isNone
|
|
|
|
tryFetch()
|
|
Iter[U].new(genNext, isFinished)
|
|
|
|
proc filter*[T](iter: Iter[T], predicate: IterFunction[T, bool]): Iter[T] =
|
|
proc wrappedPredicate(t: T): Option[T] =
|
|
if predicate(t):
|
|
some(t)
|
|
else:
|
|
T.none
|
|
|
|
mapFilter[T, T](iter, wrappedPredicate)
|
|
|
|
proc empty*[T](_: type Iter[T]): Iter[T] =
|
|
## Creates an empty Iter
|
|
##
|
|
|
|
proc genNext(): T {.raises: [CatchableError].} =
|
|
raise newException(CatchableError, "Next item requested from an empty Iter")
|
|
|
|
proc isFinished(): bool =
|
|
true
|
|
|
|
Iter[T].new(genNext, isFinished)
|