-module(chip). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([register/3, find/2, group/3, members/2, broadcast/3, start/0]). -export_type([message/3, index/0, subject_location/3, state/3]). -opaque message(GXW, GXX, GXY) :: {named_subject, gleam@erlang@process:subject({ok, gleam@erlang@process:subject(GXY)} | {error, nil}), GXW} | {named_registrant, gleam@erlang@process:subject(GXY), GXW} | {grouped_subjects, gleam@erlang@process:subject(list(gleam@erlang@process:subject(GXY))), GXX} | {grouped_registrant, gleam@erlang@process:subject(GXY), GXX} | {demonitor, index()}. -type index() :: {index, gleam@erlang@process:pid_(), gleam@erlang@process:process_monitor()}. -type subject_location(GXZ, GYA, GYB) :: {named_location, GXZ} | {grouped_location, GYA, gleam@erlang@process:subject(GYB)}. -type state(GYC, GYD, GYE) :: {state, gleam@erlang@process:subject(message(GYC, GYD, GYE)), gleam@dict:dict(GYC, gleam@erlang@process:subject(GYE)), gleam@dict:dict(GYD, gleam@set:set(gleam@erlang@process:subject(GYE))), gleam@dict:dict(gleam@erlang@process:pid_(), gleam@set:set(subject_location(GYC, GYD, GYE))), gleam@erlang@process:selector(message(GYC, GYD, GYE))}. -spec register( gleam@erlang@process:subject(message(GYU, any(), GYW)), gleam@erlang@process:subject(GYW), GYU ) -> nil. register(Registry, Subject, Name) -> gleam@erlang@process:send(Registry, {named_registrant, Subject, Name}). -spec find(gleam@erlang@process:subject(message(GZD, any(), GZE)), GZD) -> {ok, gleam@erlang@process:subject(GZE)} | {error, nil}. find(Registry, Name) -> gleam@erlang@process:call( Registry, fun(_capture) -> {named_subject, _capture, Name} end, 10 ). -spec group( gleam@erlang@process:subject(message(any(), GZJ, GZK)), gleam@erlang@process:subject(GZK), GZJ ) -> nil. group(Registry, Subject, Group) -> gleam@erlang@process:send(Registry, {grouped_registrant, Subject, Group}). -spec members(gleam@erlang@process:subject(message(any(), GZR, GZS)), GZR) -> list(gleam@erlang@process:subject(GZS)). members(Registry, Group) -> gleam@erlang@process:call( Registry, fun(_capture) -> {grouped_subjects, _capture, Group} end, 10 ). -spec broadcast( gleam@erlang@process:subject(message(any(), GZW, GZX)), GZW, fun((gleam@erlang@process:subject(GZX)) -> any()) ) -> nil. broadcast(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(HGZ, HHB, HGY), message(HGZ, HHB, HGY)). handle_init() -> Self = gleam@erlang@process:new_subject(), State = {state, Self, gleam@dict:new(), 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(6, State)}. -spec into_names(state(HAP, HAQ, HAR), HAP, gleam@erlang@process:subject(HAR)) -> state(HAP, HAQ, HAR). into_names(State, Name, Subject) -> erlang:setelement( 3, State, gleam@dict:insert(erlang:element(3, State), Name, Subject) ). -spec into_group(state(HAZ, HBA, HBB), HBA, gleam@erlang@process:subject(HBB)) -> state(HAZ, HBA, HBB). 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( 4, State, gleam@dict:update(erlang:element(4, State), Group, Add_subject) ). -spec into_tracker( state(HBJ, HBK, HBL), gleam@erlang@process:pid_(), subject_location(HBJ, HBK, HBL) ) -> state(HBJ, HBK, HBL). 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( 5, State, gleam@dict:update(erlang:element(5, State), Pid, Add_location) ). -spec into_selector( state(HBV, HBW, HBX), gleam@option:option(gleam@erlang@process:selector(message(HBV, HBW, HBX))) ) -> state(HBV, HBW, HBX). into_selector(State, Selection) -> case Selection of {some, Selector} -> erlang:setelement(6, State, Selector); none -> State end. -spec remove_from_group(state(HCJ, HCK, HCL), gleam@erlang@process:pid_()) -> state(HCJ, HCK, HCL). remove_from_group(State, Pid) -> Locations@1 = case gleam@dict:get(erlang:element(5, 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"/utf8>>, function => <<"remove_from_group"/utf8>>, line => 292}) end, gleam@list:fold( Locations@1, State, fun(State@1, Location) -> case Location of {grouped_location, Group, Subject} -> case gleam@dict:get(erlang:element(4, State@1), Group) of {ok, Subjects} -> Subjects@1 = gleam@set:delete(Subjects, Subject), Groups = gleam@dict:insert( erlang:element(4, State@1), Group, Subjects@1 ), erlang:setelement(4, State@1, Groups); {error, nil} -> erlang:error(#{gleam_error => panic, message => <<"Impossible state, couldn't find the group when removing."/utf8>>, module => <<"chip"/utf8>>, function => <<"remove_from_group"/utf8>>, line => 307}) end; {named_location, Name} -> Names = gleam@dict:delete(erlang:element(3, State@1), Name), erlang:setelement(3, State@1, Names) end end ). -spec remove_from_tracker(state(HCS, HCT, HCU), gleam@erlang@process:pid_()) -> state(HCS, HCT, HCU). remove_from_tracker(State, Pid) -> erlang:setelement( 5, State, gleam@dict:delete(erlang:element(5, State), Pid) ). -spec select_process_down( gleam@erlang@process:selector(message(HDM, HDN, HDO)), gleam@erlang@process:pid_(), gleam@erlang@process:process_monitor() ) -> gleam@erlang@process:selector(message(HDM, HDN, HDO)). 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(HDB, HDC, HDD), gleam@erlang@process:pid_()) -> gleam@option:option(gleam@erlang@process:selector(message(HDB, HDC, HDD))). monitor(State, Pid) -> case gleam@dict:get(erlang:element(5, State), Pid) of {ok, _} -> none; {error, nil} -> Monitor = gleam@erlang@process:monitor_process(Pid), Selector = select_process_down( erlang:element(6, State), Pid, Monitor ), {some, Selector} end. -spec handle_message(message(HAF, HAG, HAH), state(HAF, HAG, HAH)) -> gleam@otp@actor:next(message(HAF, HAG, HAH), state(HAF, HAG, HAH)). handle_message(Message, State) -> case Message of {named_subject, Client, Name} -> Result = gleam@dict:get(erlang:element(3, State), Name), gleam@erlang@process:send(Client, Result), gleam@otp@actor:continue(State); {named_registrant, Subject, Name@1} -> Pid = gleam@erlang@process:subject_owner(Subject), Selection = monitor(State, Pid), _pipe = State, _pipe@1 = into_names(_pipe, Name@1, Subject), _pipe@2 = into_tracker(_pipe@1, Pid, {named_location, Name@1}), _pipe@3 = into_selector(_pipe@2, Selection), {continue, _pipe@3, Selection}; {grouped_subjects, Client@1, Group} -> Subjects@1 = case gleam@dict:get(erlang:element(4, State), Group) of {ok, Subjects} -> gleam@set:to_list(Subjects); {error, nil} -> [] end, gleam@erlang@process:send(Client@1, Subjects@1), gleam@otp@actor:continue(State); {grouped_registrant, Subject@1, Group@1} -> Pid@1 = gleam@erlang@process:subject_owner(Subject@1), Selection@1 = monitor(State, Pid@1), _pipe@4 = State, _pipe@5 = into_group(_pipe@4, Group@1, Subject@1), _pipe@6 = into_tracker( _pipe@5, Pid@1, {grouped_location, Group@1, Subject@1} ), _pipe@7 = into_selector(_pipe@6, Selection@1), {continue, _pipe@7, Selection@1}; {demonitor, {index, Pid@2, Monitor}} -> gleam_erlang_ffi:demonitor(Monitor), _pipe@8 = State, _pipe@9 = remove_from_group(_pipe@8, Pid@2), _pipe@10 = remove_from_tracker(_pipe@9, Pid@2), gleam@otp@actor:continue(_pipe@10) end. -spec start() -> {ok, gleam@erlang@process:subject(message(any(), any(), any()))} | {error, gleam@otp@actor:start_error()}. start() -> gleam@otp@actor:start_spec( {spec, fun handle_init/0, 10, fun handle_message/2} ).