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