-module(lightspeed@presence).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/lightspeed/presence.gleam").
-export([new/0, join/4, leave/4, list_topic/2, get/3, key_count/2, ref_count/2, topic_labels/1, diff_topic/1, joins/1, leaves/1, join_count/1, leave_count/1, diff_label/1, meta_ref/1, meta_online_at_ms/1]).
-export_type([meta/0, entry/0, diff/0, topic_entries/0, tracker/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(" Deterministic presence tracker and diff model.\n").
-type meta() :: {meta, binary(), integer()}.
-type entry() :: {entry, binary(), list(meta())}.
-type diff() :: {diff, binary(), list(entry()), list(entry())}.
-type topic_entries() :: {topic_entries, binary(), list(entry())}.
-opaque tracker() :: {tracker, list(topic_entries())}.
-file("src/lightspeed/presence.gleam", 32).
?DOC(" New tracker.\n").
-spec new() -> tracker().
new() ->
{tracker, []}.
-file("src/lightspeed/presence.gleam", 296).
-spec has_ref(list(meta()), binary()) -> boolean().
has_ref(Metas, Ref) ->
case Metas of
[] ->
false;
[Meta | Rest] ->
case erlang:element(2, Meta) =:= Ref of
true ->
true;
false ->
has_ref(Rest, Ref)
end
end.
-file("src/lightspeed/presence.gleam", 190).
-spec join_entry(list(entry()), binary(), meta()) -> {list(entry()),
gleam@option:option(meta())}.
join_entry(Entries_rev, Key, Meta) ->
case Entries_rev of
[] ->
{[{entry, Key, [Meta]}], {some, Meta}};
[Entry | Rest] ->
case erlang:element(2, Entry) =:= Key of
true ->
case has_ref(
erlang:element(3, Entry),
erlang:element(2, Meta)
) of
true ->
{[Entry | Rest], none};
false ->
Updated = {entry,
erlang:element(2, Entry),
[Meta | erlang:element(3, Entry)]},
{[Updated | Rest], {some, Meta}}
end;
false ->
{Updated_rest, Joined} = join_entry(Rest, Key, Meta),
{[Entry | Updated_rest], Joined}
end
end.
-file("src/lightspeed/presence.gleam", 154).
-spec join_topic(list(topic_entries()), binary(), binary(), meta()) -> {list(topic_entries()),
gleam@option:option(meta())}.
join_topic(Topics_rev, Topic, Key, Meta) ->
case Topics_rev of
[] ->
{[{topic_entries, Topic, [{entry, Key, [Meta]}]}], {some, Meta}};
[Topic_entries | Rest] ->
case erlang:element(2, Topic_entries) =:= Topic of
true ->
{Entries_rev, Joined} = join_entry(
erlang:element(3, Topic_entries),
Key,
Meta
),
{[{topic_entries,
erlang:element(2, Topic_entries),
Entries_rev} |
Rest],
Joined};
false ->
{Updated_rest, Joined@1} = join_topic(
Rest,
Topic,
Key,
Meta
),
{[Topic_entries | Updated_rest], Joined@1}
end
end.
-file("src/lightspeed/presence.gleam", 37).
?DOC(" Track one join presence and return resulting diff.\n").
-spec join(tracker(), binary(), binary(), meta()) -> {tracker(), diff()}.
join(Tracker, Topic, Key, Meta) ->
{Topics_rev, Joined_meta} = join_topic(
erlang:element(2, Tracker),
Topic,
Key,
Meta
),
Joins = case Joined_meta of
{some, Joined} ->
[{entry, Key, [Joined]}];
none ->
[]
end,
{{tracker, Topics_rev}, {diff, Topic, Joins, []}}.
-file("src/lightspeed/presence.gleam", 281).
-spec remove_meta(list(meta()), binary()) -> {list(meta()),
gleam@option:option(meta())}.
remove_meta(Metas, Ref) ->
case Metas of
[] ->
{[], none};
[Meta | Rest] ->
case erlang:element(2, Meta) =:= Ref of
true ->
{Rest, {some, Meta}};
false ->
{Updated_rest, Removed} = remove_meta(Rest, Ref),
{[Meta | Updated_rest], Removed}
end
end.
-file("src/lightspeed/presence.gleam", 255).
-spec leave_entry(list(entry()), binary(), binary()) -> {list(entry()),
gleam@option:option(meta())}.
leave_entry(Entries_rev, Key, Ref) ->
case Entries_rev of
[] ->
{[], none};
[Entry | Rest] ->
case erlang:element(2, Entry) =:= Key of
true ->
{Metas, Removed} = remove_meta(
erlang:element(3, Entry),
Ref
),
case Metas of
[] ->
{Rest, Removed};
_ ->
{[{entry, erlang:element(2, Entry), Metas} | Rest],
Removed}
end;
false ->
{Updated_rest, Removed@1} = leave_entry(Rest, Key, Ref),
{[Entry | Updated_rest], Removed@1}
end
end.
-file("src/lightspeed/presence.gleam", 218).
-spec leave_topic(list(topic_entries()), binary(), binary(), binary()) -> {list(topic_entries()),
gleam@option:option(meta())}.
leave_topic(Topics_rev, Topic, Key, Ref) ->
case Topics_rev of
[] ->
{[], none};
[Topic_entries | Rest] ->
case erlang:element(2, Topic_entries) =:= Topic of
true ->
{Entries_rev, Removed_meta} = leave_entry(
erlang:element(3, Topic_entries),
Key,
Ref
),
case Entries_rev of
[] ->
{Rest, Removed_meta};
_ ->
{[{topic_entries,
erlang:element(2, Topic_entries),
Entries_rev} |
Rest],
Removed_meta}
end;
false ->
{Updated_rest, Removed_meta@1} = leave_topic(
Rest,
Topic,
Key,
Ref
),
{[Topic_entries | Updated_rest], Removed_meta@1}
end
end.
-file("src/lightspeed/presence.gleam", 57).
?DOC(" Track one leave presence and return resulting diff.\n").
-spec leave(tracker(), binary(), binary(), binary()) -> {tracker(), diff()}.
leave(Tracker, Topic, Key, Ref) ->
{Topics_rev, Removed_meta} = leave_topic(
erlang:element(2, Tracker),
Topic,
Key,
Ref
),
Leaves = case Removed_meta of
{some, Left} ->
[{entry, Key, [Left]}];
none ->
[]
end,
{{tracker, Topics_rev}, {diff, Topic, [], Leaves}}.
-file("src/lightspeed/presence.gleam", 307).
-spec find_topic_entries(list(topic_entries()), binary()) -> list(entry()).
find_topic_entries(Topics_rev, Topic) ->
case Topics_rev of
[] ->
[];
[Topic_entries | Rest] ->
case erlang:element(2, Topic_entries) =:= Topic of
true ->
erlang:element(3, Topic_entries);
false ->
find_topic_entries(Rest, Topic)
end
end.
-file("src/lightspeed/presence.gleam", 77).
?DOC(" List full presence state for one topic.\n").
-spec list_topic(tracker(), binary()) -> list(entry()).
list_topic(Tracker, Topic) ->
find_topic_entries(erlang:element(2, Tracker), Topic).
-file("src/lightspeed/presence.gleam", 321).
-spec find_entry(list(entry()), binary()) -> gleam@option:option(entry()).
find_entry(Entries, Key) ->
case Entries of
[] ->
none;
[Entry | Rest] ->
case erlang:element(2, Entry) =:= Key of
true ->
{some, Entry};
false ->
find_entry(Rest, Key)
end
end.
-file("src/lightspeed/presence.gleam", 82).
?DOC(" Get one key presence entry.\n").
-spec get(tracker(), binary(), binary()) -> gleam@option:option(entry()).
get(Tracker, Topic, Key) ->
_pipe = list_topic(Tracker, Topic),
find_entry(_pipe, Key).
-file("src/lightspeed/presence.gleam", 88).
?DOC(" Number of tracked keys in one topic.\n").
-spec key_count(tracker(), binary()) -> integer().
key_count(Tracker, Topic) ->
erlang:length(list_topic(Tracker, Topic)).
-file("src/lightspeed/presence.gleam", 332).
-spec count_refs(list(entry()), integer()) -> integer().
count_refs(Entries, Total) ->
case Entries of
[] ->
Total;
[Entry | Rest] ->
count_refs(Rest, Total + erlang:length(erlang:element(3, Entry)))
end.
-file("src/lightspeed/presence.gleam", 93).
?DOC(" Number of tracked refs in one topic.\n").
-spec ref_count(tracker(), binary()) -> integer().
ref_count(Tracker, Topic) ->
_pipe = list_topic(Tracker, Topic),
count_refs(_pipe, 0).
-file("src/lightspeed/presence.gleam", 99).
?DOC(" Stable topic labels for logs and tests.\n").
-spec topic_labels(tracker()) -> list(binary()).
topic_labels(Tracker) ->
_pipe = erlang:element(2, Tracker),
gleam@list:map(
_pipe,
fun(Topic_entries) ->
<<<<<<<<(erlang:element(2, Topic_entries))/binary, ":keys="/utf8>>/binary,
(erlang:integer_to_binary(
erlang:length(erlang:element(3, Topic_entries))
))/binary>>/binary,
":refs="/utf8>>/binary,
(erlang:integer_to_binary(
count_refs(erlang:element(3, Topic_entries), 0)
))/binary>>
end
).
-file("src/lightspeed/presence.gleam", 111).
?DOC(" Diff topic.\n").
-spec diff_topic(diff()) -> binary().
diff_topic(Diff) ->
erlang:element(2, Diff).
-file("src/lightspeed/presence.gleam", 116).
?DOC(" Joined entries.\n").
-spec joins(diff()) -> list(entry()).
joins(Diff) ->
erlang:element(3, Diff).
-file("src/lightspeed/presence.gleam", 121).
?DOC(" Left entries.\n").
-spec leaves(diff()) -> list(entry()).
leaves(Diff) ->
erlang:element(4, Diff).
-file("src/lightspeed/presence.gleam", 126).
?DOC(" Number of join entries in one diff.\n").
-spec join_count(diff()) -> integer().
join_count(Diff) ->
erlang:length(erlang:element(3, Diff)).
-file("src/lightspeed/presence.gleam", 131).
?DOC(" Number of leave entries in one diff.\n").
-spec leave_count(diff()) -> integer().
leave_count(Diff) ->
erlang:length(erlang:element(4, Diff)).
-file("src/lightspeed/presence.gleam", 136).
?DOC(" Stable diff label for fixtures.\n").
-spec diff_label(diff()) -> binary().
diff_label(Diff) ->
<<<<<<<<(erlang:element(2, Diff))/binary, ":joins="/utf8>>/binary,
(erlang:integer_to_binary(join_count(Diff)))/binary>>/binary,
":leaves="/utf8>>/binary,
(erlang:integer_to_binary(leave_count(Diff)))/binary>>.
-file("src/lightspeed/presence.gleam", 145).
?DOC(" Meta reference.\n").
-spec meta_ref(meta()) -> binary().
meta_ref(Meta) ->
erlang:element(2, Meta).
-file("src/lightspeed/presence.gleam", 150).
?DOC(" Meta online timestamp.\n").
-spec meta_online_at_ms(meta()) -> integer().
meta_online_at_ms(Meta) ->
erlang:element(3, Meta).