-module(lightspeed@stream).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/lightspeed/stream.gleam").
-export([new/1, item/2, options/0, with_at/2, with_limit/2, with_reset/2, target/1, entries/1, stream/3, stream_insert/4, stream_delete/2]).
-export_type([stream_item/0, stream/0, stream_options/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(" LiveView-style stream compatibility helpers.\n").
-type stream_item() :: {stream_item, binary(), binary()}.
-opaque stream() :: {stream, binary(), list(stream_item())}.
-type stream_options() :: {stream_options,
integer(),
gleam@option:option(integer()),
boolean()}.
-file("src/lightspeed/stream.gleam", 23).
?DOC(" Create a new empty stream state for a target container.\n").
-spec new(binary()) -> stream().
new(Target) ->
{stream, Target, []}.
-file("src/lightspeed/stream.gleam", 28).
?DOC(" Build one stream item.\n").
-spec item(binary(), binary()) -> stream_item().
item(Id, Html) ->
{stream_item, Id, Html}.
-file("src/lightspeed/stream.gleam", 36).
?DOC(
" Default stream options:\n"
" - at: -1 (append)\n"
" - limit: none\n"
" - reset: false\n"
).
-spec options() -> stream_options().
options() ->
{stream_options, -1, none, false}.
-file("src/lightspeed/stream.gleam", 41).
?DOC(" Override insertion position.\n").
-spec with_at(stream_options(), integer()) -> stream_options().
with_at(Options, At) ->
{stream_options, At, erlang:element(3, Options), erlang:element(4, Options)}.
-file("src/lightspeed/stream.gleam", 47).
?DOC(
" Override stream limit.\n"
" Positive keeps first N; negative keeps last N.\n"
).
-spec with_limit(stream_options(), integer()) -> stream_options().
with_limit(Options, Limit) ->
{stream_options,
erlang:element(2, Options),
{some, Limit},
erlang:element(4, Options)}.
-file("src/lightspeed/stream.gleam", 52).
?DOC(" Override reset behavior.\n").
-spec with_reset(stream_options(), boolean()) -> stream_options().
with_reset(Options, Reset) ->
{stream_options,
erlang:element(2, Options),
erlang:element(3, Options),
Reset}.
-file("src/lightspeed/stream.gleam", 57).
?DOC(" Current target selector.\n").
-spec target(stream()) -> binary().
target(Stream_state) ->
erlang:element(2, Stream_state).
-file("src/lightspeed/stream.gleam", 62).
?DOC(" Current entries in render order.\n").
-spec entries(stream()) -> list(stream_item()).
entries(Stream_state) ->
erlang:element(3, Stream_state).
-file("src/lightspeed/stream.gleam", 301).
-spec key_order(list(stream_item()), list(binary())) -> list(binary()).
key_order(Current, Keys_rev) ->
case Current of
[] ->
lists:reverse(Keys_rev);
[{stream_item, Id, _} | Rest] ->
key_order(Rest, [Id | Keys_rev])
end.
-file("src/lightspeed/stream.gleam", 354).
-spec concat(list(lightspeed@diff:patch()), list(lightspeed@diff:patch())) -> list(lightspeed@diff:patch()).
concat(Left, Right) ->
case Left of
[] ->
Right;
[Head | Rest] ->
[Head | concat(Rest, Right)]
end.
-file("src/lightspeed/stream.gleam", 155).
-spec keyed_reorder_patch(
binary(),
list(stream_item()),
list(lightspeed@diff:patch())
) -> list(lightspeed@diff:patch()).
keyed_reorder_patch(Target, Current, Keyed) ->
concat(Keyed, [{reorder_keyed, Target, key_order(Current, [])}]).
-file("src/lightspeed/stream.gleam", 332).
-spec contains_id(list(stream_item()), binary()) -> boolean().
contains_id(Entries, Id) ->
case Entries of
[] ->
false;
[Entry | Rest] ->
case erlang:element(2, Entry) =:= Id of
true ->
true;
false ->
contains_id(Rest, Id)
end
end.
-file("src/lightspeed/stream.gleam", 311).
-spec new_ids_only_at_tail(list(stream_item()), list(stream_item()), boolean()) -> boolean().
new_ids_only_at_tail(Current, Previous, Saw_new) ->
case Current of
[] ->
true;
[{stream_item, Id, _} | Rest] ->
case contains_id(Previous, Id) of
true ->
case Saw_new of
true ->
false;
false ->
new_ids_only_at_tail(Rest, Previous, false)
end;
false ->
new_ids_only_at_tail(Rest, Previous, true)
end
end.
-file("src/lightspeed/stream.gleam", 285).
-spec collect_existing_ids(
list(stream_item()),
list(stream_item()),
list(binary())
) -> list(binary()).
collect_existing_ids(Left, Right, Ids_rev) ->
case Left of
[] ->
lists:reverse(Ids_rev);
[{stream_item, Id, _} | Rest] ->
case contains_id(Right, Id) of
true ->
collect_existing_ids(Rest, Right, [Id | Ids_rev]);
false ->
collect_existing_ids(Rest, Right, Ids_rev)
end
end.
-file("src/lightspeed/stream.gleam", 146).
-spec supports_incremental(list(stream_item()), list(stream_item())) -> boolean().
supports_incremental(Previous, Current) ->
(collect_existing_ids(Current, Previous, []) =:= collect_existing_ids(
Previous,
Current,
[]
))
andalso new_ids_only_at_tail(Current, Previous, false).
-file("src/lightspeed/stream.gleam", 344).
-spec to_keyed_nodes(list(stream_item())) -> list(lightspeed@diff:keyed_node()).
to_keyed_nodes(Entries) ->
case Entries of
[] ->
[];
[{stream_item, Id, Html} | Rest] ->
[lightspeed@diff:keyed_node(Id, Html) | to_keyed_nodes(Rest)]
end.
-file("src/lightspeed/stream.gleam", 123).
-spec patch_plan(binary(), list(stream_item()), list(stream_item())) -> list(lightspeed@diff:patch()).
patch_plan(Target, Previous, Current) ->
case Previous =:= Current of
true ->
[];
false ->
Keyed = lightspeed@diff:keyed_patch_plan(
Target,
to_keyed_nodes(Previous),
to_keyed_nodes(Current)
),
case supports_incremental(Previous, Current) of
true ->
Keyed;
false ->
keyed_reorder_patch(Target, Current, Keyed)
end
end.
-file("src/lightspeed/stream.gleam", 274).
-spec drop(list(stream_item()), integer()) -> list(stream_item()).
drop(Entries, Count) ->
case Count =< 0 of
true ->
Entries;
false ->
case Entries of
[] ->
[];
[_ | Rest] ->
drop(Rest, Count - 1)
end
end.
-file("src/lightspeed/stream.gleam", 265).
-spec take_last(list(stream_item()), integer()) -> list(stream_item()).
take_last(Entries, Count) ->
Length = erlang:length(Entries),
case Count >= Length of
true ->
Entries;
false ->
drop(Entries, Length - Count)
end.
-file("src/lightspeed/stream.gleam", 254).
-spec take(list(stream_item()), integer()) -> list(stream_item()).
take(Entries, Count) ->
case Count =< 0 of
true ->
[];
false ->
case Entries of
[] ->
[];
[Head | Rest] ->
[Head | take(Rest, Count - 1)]
end
end.
-file("src/lightspeed/stream.gleam", 235).
-spec apply_limit(list(stream_item()), gleam@option:option(integer())) -> list(stream_item()).
apply_limit(Entries, Limit) ->
case Limit of
none ->
Entries;
{some, Value} ->
case Value of
0 ->
[];
_ ->
case Value > 0 of
true ->
take(Entries, Value);
false ->
take_last(Entries, 0 - Value)
end
end
end.
-file("src/lightspeed/stream.gleam", 205).
-spec insert_at(list(stream_item()), stream_item(), integer()) -> list(stream_item()).
insert_at(Entries, Entry, Index) ->
case Index =< 0 of
true ->
[Entry | Entries];
false ->
case Entries of
[] ->
[Entry];
[Head | Rest] ->
[Head | insert_at(Rest, Entry, Index - 1)]
end
end.
-file("src/lightspeed/stream.gleam", 194).
-spec resolve_insert_index(integer(), integer()) -> integer().
resolve_insert_index(At, Length) ->
case At < 0 of
true ->
Length;
false ->
case At > Length of
true ->
Length;
false ->
At
end
end.
-file("src/lightspeed/stream.gleam", 220).
-spec remove_entry(list(stream_item()), binary()) -> list(stream_item()).
remove_entry(Entries, Entry_id) ->
case Entries of
[] ->
[];
[Entry | Rest] ->
case erlang:element(2, Entry) =:= Entry_id of
true ->
remove_entry(Rest, Entry_id);
false ->
[Entry | remove_entry(Rest, Entry_id)]
end
end.
-file("src/lightspeed/stream.gleam", 184).
-spec insert_one(list(stream_item()), stream_item(), integer()) -> list(stream_item()).
insert_one(Entries, Entry, At) ->
Without_existing = remove_entry(Entries, erlang:element(2, Entry)),
Index = resolve_insert_index(At, erlang:length(Without_existing)),
insert_at(Without_existing, Entry, Index).
-file("src/lightspeed/stream.gleam", 165).
-spec insert_many(list(stream_item()), list(stream_item()), integer()) -> list(stream_item()).
insert_many(Entries, Incoming, At) ->
case Incoming of
[] ->
Entries;
[Entry | Rest] ->
Next = insert_one(Entries, Entry, At),
Next_at = case At < 0 of
true ->
At;
false ->
At + 1
end,
insert_many(Next, Rest, Next_at)
end.
-file("src/lightspeed/stream.gleam", 67).
?DOC(" Apply `stream/4` semantics.\n").
-spec stream(stream(), list(stream_item()), stream_options()) -> {stream(),
list(lightspeed@diff:patch())}.
stream(Stream_state, Incoming, Options) ->
Previous = erlang:element(3, Stream_state),
Base = case erlang:element(4, Options) of
true ->
[];
false ->
Previous
end,
Merged = insert_many(Base, Incoming, erlang:element(2, Options)),
Limited = apply_limit(Merged, erlang:element(3, Options)),
Next = {stream, erlang:element(2, Stream_state), Limited},
Patches = patch_plan(erlang:element(2, Stream_state), Previous, Limited),
{Next, Patches}.
-file("src/lightspeed/stream.gleam", 88).
?DOC(" Apply `stream_insert/4` semantics.\n").
-spec stream_insert(
stream(),
stream_item(),
integer(),
gleam@option:option(integer())
) -> {stream(), list(lightspeed@diff:patch())}.
stream_insert(Stream_state, Entry, At, Limit) ->
stream(Stream_state, [Entry], {stream_options, At, Limit, false}).
-file("src/lightspeed/stream.gleam", 102).
?DOC(" Apply `stream_delete/2` semantics.\n").
-spec stream_delete(stream(), binary()) -> {stream(),
list(lightspeed@diff:patch())}.
stream_delete(Stream_state, Entry_id) ->
Previous = erlang:element(3, Stream_state),
Current = remove_entry(Previous, Entry_id),
Next = {stream, erlang:element(2, Stream_state), Current},
Patches = case Previous =:= Current of
true ->
[];
false ->
lightspeed@diff:keyed_patch_plan(
erlang:element(2, Stream_state),
to_keyed_nodes(Previous),
to_keyed_nodes(Current)
)
end,
{Next, Patches}.