Current section
Files
Jump to
Current section
Files
lib/iter/iterable/by_chunker.ex
defmodule Iter.Iterable.ByChunker do
defstruct iterable: nil, chunker: nil, state: :none, buffer: []
@moduledoc """
An iterable that chunks elements by subsequent return values of `fun`.
"""
alias Iter.{Impl, IntoIterable, Iterable}
@type t :: %__MODULE__{
iterable: Iterable.t(),
chunker: (Iterable.element() -> any),
state: {:ok, any} | :none,
buffer: []
}
@doc """
Creates an iterable that chunks elements by subsequent return values of `fun`.
"""
@spec new(Iterable.t(), (Iterable.element() -> any)) :: t
def new(iterable, chunker) when is_function(chunker, 1),
do: %__MODULE__{iterable: iterable, chunker: chunker}
defimpl Iterable do
use Impl
@doc false
@impl true
def next(chunk_by) when chunk_by.buffer == [] do
with {:ok, element, iterable} <- Iterable.next(chunk_by.iterable) do
next(%{
chunk_by
| iterable: iterable,
state: {:ok, chunk_by.chunker.(element)},
buffer: [element]
})
end
end
def next(chunk_by) do
case Iterable.next(chunk_by.iterable) do
{:ok, element, iterable} ->
chunk_result = chunk_by.chunker.(element)
if {:ok, chunk_result} == chunk_by.state do
next(%{chunk_by | iterable: iterable, buffer: [element | chunk_by.buffer]})
else
{:ok, :lists.reverse(chunk_by.buffer),
%{chunk_by | iterable: iterable, state: {:ok, chunk_result}, buffer: [element]}}
end
:done ->
{:ok, :lists.reverse(chunk_by.buffer), Iterable.Empty.new()}
end
end
end
defimpl IntoIterable do
@doc false
def into_iterable(self), do: self
end
end