-module(chip). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([new/1, tag/2, group/2, register/2, find/2, dispatch/2, dispatch_group/3, start/0]). -export_type([message/3, chip/3, state/3]). -opaque message(GXW, GXX, GXY) :: {register, chip(GXW, GXX, GXY)} | {demonitor, gleam@erlang@process:process_monitor(), chip(GXW, GXX, GXY)} | {lookup, gleam@erlang@process:subject({ok, gleam@erlang@process:subject(GXW)} | {error, nil}), GXX} | {members, gleam@erlang@process:subject(list(gleam@erlang@process:subject(GXW)))} | {members_at, gleam@erlang@process:subject(list(gleam@erlang@process:subject(GXW))), GXY}. -opaque chip(GXZ, GYA, GYB) :: {chip, gleam@erlang@process:subject(GXZ), gleam@option:option(GYA), gleam@option:option(GYB)}. -type state(GYC, GYD, GYE) :: {state, gleam@erlang@process:selector(message(GYC, GYD, GYE)), gleam@set:set(gleam@erlang@process:subject(GYC)), gleam@dict:dict(GYD, gleam@erlang@process:subject(GYC)), gleam@dict:dict(GYE, gleam@set:set(gleam@erlang@process:subject(GYC)))}. -spec new(gleam@erlang@process:subject(GYU)) -> chip(GYU, any(), any()). new(Subject) -> {chip, Subject, none, none}. -spec tag(chip(GZB, GZC, GZD), GZC) -> chip(GZB, GZC, GZD). tag(Registrant, Tag) -> erlang:setelement(3, Registrant, {some, Tag}). -spec group(chip(GZK, GZL, GZM), GZM) -> chip(GZK, GZL, GZM). group(Registrant, Group) -> erlang:setelement(4, Registrant, {some, Group}). -spec register( gleam@erlang@process:subject(message(GZT, GZU, GZV)), chip(GZT, GZU, GZV) ) -> nil. register(Registry, Registrant) -> gleam@erlang@process:send(Registry, {register, Registrant}). -spec find(gleam@erlang@process:subject(message(HAC, HAD, any())), HAD) -> {ok, gleam@erlang@process:subject(HAC)} | {error, nil}. find(Registry, Tag) -> gleam@erlang@process:call( Registry, fun(_capture) -> {lookup, _capture, Tag} end, 10 ). -spec dispatch( gleam@erlang@process:subject(message(HAM, any(), any())), fun((gleam@erlang@process:subject(HAM)) -> any()) ) -> nil. dispatch(Registry, Callback) -> Subjects = gleam@erlang@process:call( Registry, fun(_capture) -> {members, _capture} end, 10 ), gleam@list:each(Subjects, Callback). -spec dispatch_group( gleam@erlang@process:subject(message(HAU, any(), HAW)), HAW, fun((gleam@erlang@process:subject(HAU)) -> any()) ) -> nil. dispatch_group(Registry, Group, Callback) -> Subjects = gleam@erlang@process:call( Registry, fun(_capture) -> {members_at, _capture, Group} end, 10 ), gleam@list:each(Subjects, Callback). -spec init() -> gleam@otp@actor:init_result(state(HBC, HBD, HBE), message(HBC, HBD, HBE)). init() -> Selector = gleam_erlang_ffi:new_selector(), {ready, {state, Selector, gleam@set:new(), gleam@dict:new(), gleam@dict:new()}, Selector}. -spec monitor(state(HCE, HCF, HCG), chip(HCE, HCF, HCG)) -> state(HCE, HCF, HCG). monitor(State, Registrant) -> Pid = gleam@erlang@process:subject_owner(erlang:element(2, Registrant)), Monitor = gleam@erlang@process:monitor_process(Pid), On_process_down = fun(_) -> {demonitor, Monitor, Registrant} end, Selector = begin _pipe = erlang:element(2, State), gleam@erlang@process:selecting_process_down( _pipe, Monitor, On_process_down ) end, erlang:setelement(2, State, Selector). -spec into_registered(state(HCQ, HCR, HCS), chip(HCQ, HCR, HCS)) -> state(HCQ, HCR, HCS). into_registered(State, Registrant) -> Subjects = erlang:element(3, State), Subject = erlang:element(2, Registrant), erlang:setelement(3, State, gleam@set:insert(Subjects, Subject)). -spec into_tagged(state(HDC, HDD, HDE), chip(HDC, HDD, HDE)) -> state(HDC, HDD, HDE). into_tagged(State, Registrant) -> case Registrant of {chip, Subject, {some, Tag}, _} -> Subjects = erlang:element(4, State), Tagged = gleam@dict:insert(Subjects, Tag, Subject), erlang:setelement(4, State, Tagged); {chip, _, none, _} -> State end. -spec into_grouped(state(HDO, HDP, HDQ), chip(HDO, HDP, HDQ)) -> state(HDO, HDP, HDQ). into_grouped(State, Registrant) -> Add_subject = fun(Option) -> case Option of {some, Subjects} -> gleam@set:insert(Subjects, erlang:element(2, Registrant)); none -> gleam@set:insert(gleam@set:new(), erlang:element(2, Registrant)) end end, case Registrant of {chip, _, _, {some, Group}} -> Grouped = gleam@dict:update( erlang:element(5, State), Group, Add_subject ), erlang:setelement(5, State, Grouped); {chip, _, _, none} -> State end. -spec remove_from_registered(state(HEA, HEB, HEC), chip(HEA, HEB, HEC)) -> state(HEA, HEB, HEC). remove_from_registered(State, Registrant) -> Registered = gleam@set:delete( erlang:element(3, State), erlang:element(2, Registrant) ), erlang:setelement(3, State, Registered). -spec remove_from_tagged(state(HEM, HEN, HEO), chip(HEM, HEN, HEO)) -> state(HEM, HEN, HEO). remove_from_tagged(State, Registrant) -> case Registrant of {chip, _, {some, Tag}, _} -> Tagged = gleam@dict:delete(erlang:element(4, State), Tag), erlang:setelement(4, State, Tagged); {chip, _, none, _} -> State end. -spec remove_from_grouped(state(HEY, HEZ, HFA), chip(HEY, HEZ, HFA)) -> state(HEY, HEZ, HFA). remove_from_grouped(State, Registrant) -> case Registrant of {chip, _, _, {some, Group}} -> case gleam@dict:get(erlang:element(5, State), Group) of {ok, Subjects} -> Subjects@1 = gleam@set:delete( Subjects, erlang:element(2, Registrant) ), Grouped = gleam@dict:insert( erlang:element(5, State), Group, Subjects@1 ), erlang:setelement(5, State, Grouped); {error, nil} -> erlang:error(#{gleam_error => panic, message => <<"Impossible state, group was not found on remove_from_grouped."/utf8>>, module => <<"chip"/utf8>>, function => <<"remove_from_grouped"/utf8>>, line => 373}) end; {chip, _, _, none} -> State end. -spec loop(message(HBN, HBO, HBP), state(HBN, HBO, HBP)) -> gleam@otp@actor:next(message(HBN, HBO, HBP), state(HBN, HBO, HBP)). loop(Message, State) -> case Message of {register, Registrant} -> State@1 = monitor(State, Registrant), State@2 = begin _pipe = State@1, _pipe@1 = monitor(_pipe, Registrant), _pipe@2 = into_registered(_pipe@1, Registrant), _pipe@3 = into_tagged(_pipe@2, Registrant), into_grouped(_pipe@3, Registrant) end, {continue, State@2, {some, erlang:element(2, State@2)}}; {demonitor, Monitor, Registrant@1} -> gleam_erlang_ffi:demonitor(Monitor), _pipe@4 = State, _pipe@5 = remove_from_registered(_pipe@4, Registrant@1), _pipe@6 = remove_from_tagged(_pipe@5, Registrant@1), _pipe@7 = remove_from_grouped(_pipe@6, Registrant@1), {continue, _pipe@7, none}; {lookup, Client, Tag} -> Result = gleam@dict:get(erlang:element(4, State), Tag), gleam@erlang@process:send(Client, Result), {continue, State, none}; {members, Client@1} -> Subjects = gleam@set:to_list(erlang:element(3, State)), gleam@erlang@process:send(Client@1, Subjects), {continue, State, none}; {members_at, Client@2, Group} -> Subjects@2 = case gleam@dict:get(erlang:element(5, State), Group) of {ok, Subjects@1} -> gleam@set:to_list(Subjects@1); {error, nil} -> [] end, gleam@erlang@process:send(Client@2, Subjects@2), {continue, State, none} 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 init/0, 10, fun loop/2}).