-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}.