-module(chip@group). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([register/3, members/2, dispatch/3, start/0]). -export_type([message/2, index/0, location/2, state/2]). -opaque message(GXW, GXX) :: {grouped_subjects, gleam@erlang@process:subject(list(gleam@erlang@process:subject(GXX))), GXW} | {grouped_registrant, gleam@erlang@process:subject(GXX), GXW} | {demonitor, index()}. -type index() :: {index, gleam@erlang@process:pid_(), gleam@erlang@process:process_monitor()}. -type location(GXY, GXZ) :: {location, GXY, gleam@erlang@process:subject(GXZ)}. -type state(GYA, GYB) :: {state, gleam@erlang@process:subject(message(GYA, GYB)), gleam@dict:dict(GYA, gleam@set:set(gleam@erlang@process:subject(GYB))), gleam@dict:dict(gleam@erlang@process:pid_(), gleam@set:set(location(GYA, GYB))), gleam@erlang@process:selector(message(GYA, GYB))}. -spec register( gleam@erlang@process:subject(message(GYN, GYO)), gleam@erlang@process:subject(GYO), GYN ) -> nil. register(Registry, Subject, Group) -> gleam@erlang@process:send(Registry, {grouped_registrant, Subject, Group}). -spec members(gleam@erlang@process:subject(message(GYU, GYV)), GYU) -> list(gleam@erlang@process:subject(GYV)). members(Registry, Group) -> gleam@erlang@process:call( Registry, fun(_capture) -> {grouped_subjects, _capture, Group} end, 10 ). -spec dispatch( gleam@erlang@process:subject(message(GYY, GYZ)), GYY, fun((gleam@erlang@process:subject(GYZ)) -> any()) ) -> nil. dispatch(Registry, Group, Callback) -> Subjects = members(Registry, Group), gleam@list:each(Subjects, fun(Subject) -> Callback(Subject) end). -spec handle_init() -> gleam@otp@actor:init_result(state(HDL, HDK), message(HDL, HDK)). handle_init() -> Self = gleam@erlang@process:new_subject(), State = {state, Self, gleam@dict:new(), gleam@dict:new(), begin _pipe = gleam_erlang_ffi:new_selector(), gleam@erlang@process:selecting( _pipe, Self, fun gleam@function:identity/1 ) end}, {ready, State, erlang:element(5, State)}. -spec into_group(state(GZN, GZO), GZN, gleam@erlang@process:subject(GZO)) -> state(GZN, GZO). into_group(State, Group, Subject) -> Add_subject = fun(Option) -> case Option of {some, Subjects} -> gleam@set:insert(Subjects, Subject); none -> gleam@set:insert(gleam@set:new(), Subject) end end, erlang:setelement( 3, State, gleam@dict:update(erlang:element(3, State), Group, Add_subject) ). -spec into_tracker( state(GZU, GZV), gleam@erlang@process:pid_(), location(GZU, GZV) ) -> state(GZU, GZV). into_tracker(State, Pid, Location) -> Add_location = fun(Option) -> case Option of {some, Locations} -> gleam@set:insert(Locations, Location); none -> gleam@set:insert(gleam@set:new(), Location) end end, erlang:setelement( 4, State, gleam@dict:update(erlang:element(4, State), Pid, Add_location) ). -spec into_selector( state(HAC, HAD), gleam@option:option(gleam@erlang@process:selector(message(HAC, HAD))) ) -> state(HAC, HAD). into_selector(State, Selection) -> case Selection of {some, Selector} -> erlang:setelement(5, State, Selector); none -> State end. -spec remove_from_group(state(HAM, HAN), gleam@erlang@process:pid_()) -> state(HAM, HAN). remove_from_group(State, Pid) -> Locations@1 = case gleam@dict:get(erlang:element(4, State), Pid) of {ok, Locations} -> gleam@set:to_list(Locations); {error, nil} -> erlang:error(#{gleam_error => panic, message => <<"Impossible state, couldn't find a pid when removing from group."/utf8>>, module => <<"chip/group"/utf8>>, function => <<"remove_from_group"/utf8>>, line => 228}) end, gleam@list:fold( Locations@1, State, fun(State@1, Location) -> {location, Group, Subject} = Location, case gleam@dict:get(erlang:element(3, State@1), Group) of {ok, Subjects} -> Subjects@1 = gleam@set:delete(Subjects, Subject), Groups = gleam@dict:insert( erlang:element(3, State@1), Group, Subjects@1 ), erlang:setelement(3, State@1, Groups); {error, nil} -> erlang:error(#{gleam_error => panic, message => <<"Impossible state, couldn't find the group when removing."/utf8>>, module => <<"chip/group"/utf8>>, function => <<"remove_from_group"/utf8>>, line => 243}) end end ). -spec remove_from_tracker(state(HAS, HAT), gleam@erlang@process:pid_()) -> state(HAS, HAT). remove_from_tracker(State, Pid) -> erlang:setelement( 4, State, gleam@dict:delete(erlang:element(4, State), Pid) ). -spec select_process_down( gleam@erlang@process:selector(message(HBG, HBH)), gleam@erlang@process:pid_(), gleam@erlang@process:process_monitor() ) -> gleam@erlang@process:selector(message(HBG, HBH)). select_process_down(Selector, Pid, Monitor) -> Index = {index, Pid, Monitor}, Handle = fun(_) -> {demonitor, Index} end, gleam@erlang@process:selecting_process_down(Selector, Monitor, Handle). -spec monitor(state(HAY, HAZ), gleam@erlang@process:pid_()) -> gleam@option:option(gleam@erlang@process:selector(message(HAY, HAZ))). monitor(State, Pid) -> case gleam@dict:get(erlang:element(4, State), Pid) of {ok, _} -> none; {error, nil} -> Monitor = gleam@erlang@process:monitor_process(Pid), Selector = select_process_down( erlang:element(5, State), Pid, Monitor ), {some, Selector} end. -spec handle_message(message(GZG, GZH), state(GZG, GZH)) -> gleam@otp@actor:next(message(GZG, GZH), state(GZG, GZH)). handle_message(Message, State) -> case Message of {grouped_subjects, Client, Group} -> Subjects@1 = case gleam@dict:get(erlang:element(3, State), Group) of {ok, Subjects} -> gleam@set:to_list(Subjects); {error, nil} -> [] end, gleam@erlang@process:send(Client, Subjects@1), gleam@otp@actor:continue(State); {grouped_registrant, Subject, Group@1} -> Pid = gleam@erlang@process:subject_owner(Subject), Selection = monitor(State, Pid), _pipe = State, _pipe@1 = into_group(_pipe, Group@1, Subject), _pipe@2 = into_tracker(_pipe@1, Pid, {location, Group@1, Subject}), _pipe@3 = into_selector(_pipe@2, Selection), {continue, _pipe@3, Selection}; {demonitor, {index, Pid@1, Monitor}} -> gleam_erlang_ffi:demonitor(Monitor), _pipe@4 = State, _pipe@5 = remove_from_group(_pipe@4, Pid@1), _pipe@6 = remove_from_tracker(_pipe@5, Pid@1), gleam@otp@actor:continue(_pipe@6) end. -spec start() -> {ok, gleam@erlang@process:subject(message(any(), any()))} | {error, gleam@otp@actor:start_error()}. start() -> gleam@otp@actor:start_spec( {spec, fun handle_init/0, 10, fun handle_message/2} ).