-module(chip). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([new/1, tag/2, group/2, register/2, dispatch/2, dispatch_group/3, stop/1, find/2, start/0]). -export_type([message/3, chip/3, state/3, process_down/0, table/0, option/0, step/0, search/1]). -opaque message(HNE, HNF, HNG) :: {register, chip(HNE, HNF, HNG)} | {demonitor, gleam@erlang:reference_(), gleam@erlang@process:pid_()} | {find, gleam@erlang@process:subject(gleam@erlang:reference_())} | {dispatch, fun((gleam@erlang@process:subject(HNE)) -> nil)} | {dispatch_group, fun((gleam@erlang@process:subject(HNE)) -> nil), HNG} | {no_operation, gleam@dynamic:dynamic_()} | stop. -opaque chip(HNH, HNI, HNJ) :: {chip, gleam@erlang@process:subject(HNH), gleam@option:option(HNI), gleam@option:option(HNJ)}. -type state(HNK, HNL, HNM) :: {state, integer(), gleam@erlang:reference_(), gleam@erlang:reference_(), gleam@erlang:reference_()} | {gleam_phantom, HNK, HNL, HNM}. -type process_down() :: {process_down, gleam@erlang:reference_(), gleam@erlang@process:pid_()}. -type table() :: chip_registry | chip_registry_tagged | chip_registry_grouped. -type option() :: protected | set | bag. -type step() :: any(). -type search(HNN) :: {partial, list(HNN), step()} | {end_of_table, list(HNN)}. -spec new(gleam@erlang@process:subject(HOD)) -> chip(HOD, any(), any()). new(Subject) -> {chip, Subject, none, none}. -spec tag(chip(HOK, HOL, HOM), HOL) -> chip(HOK, HOL, HOM). tag(Registrant, Tag) -> erlang:setelement(3, Registrant, {some, Tag}). -spec group(chip(HOT, HOU, HOV), HOV) -> chip(HOT, HOU, HOV). group(Registrant, Group) -> erlang:setelement(4, Registrant, {some, Group}). -spec register( gleam@erlang@process:subject(message(HPC, HPD, HPE)), chip(HPC, HPD, HPE) ) -> nil. register(Registry, Registrant) -> gleam@erlang@process:send(Registry, {register, Registrant}). -spec dispatch( gleam@erlang@process:subject(message(HPV, any(), any())), fun((gleam@erlang@process:subject(HPV)) -> nil) ) -> nil. dispatch(Registry, Callback) -> gleam@erlang@process:send(Registry, {dispatch, Callback}). -spec dispatch_group( gleam@erlang@process:subject(message(HQC, any(), HQE)), HQE, fun((gleam@erlang@process:subject(HQC)) -> nil) ) -> nil. dispatch_group(Registry, Group, Callback) -> gleam@erlang@process:send(Registry, {dispatch_group, Callback, Group}). -spec stop(gleam@erlang@process:subject(message(any(), any(), any()))) -> nil. stop(Registry) -> gleam@erlang@process:send(Registry, stop). -spec run_batch( list(gleam@erlang@process:subject(HSV)), fun((gleam@erlang@process:subject(HSV)) -> nil) ) -> nil. run_batch(Subjects, Callback) -> _pipe = Subjects, _pipe@1 = gleam@list:map( _pipe, fun(Subject) -> gleam@otp@task:async(fun() -> Callback(Subject) end) end ), gleam@list:each(_pipe@1, fun(Task) -> gleam@otp@task:await(Task, 5000) end). -spec match_into(integer()) -> gleam@erlang@atom:atom_(). match_into(N) -> erlang:binary_to_atom(<<"$"/utf8, (gleam@int:to_string(N))/binary>>). -spec match_any() -> gleam@erlang@atom:atom_(). match_any() -> erlang:binary_to_atom(<<"_"/utf8>>). -spec insert(state(HRR, HRS, HRT), chip(HRR, HRS, HRT)) -> nil. insert(State, Registrant) -> Pid = gleam@erlang@process:subject_owner(erlang:element(2, Registrant)), _ = gleam@erlang@process:monitor_process(Pid), _assert_subject = ets:insert( erlang:element(3, State), {erlang:element(2, Registrant), Pid} ), true = case _assert_subject of true -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"chip"/utf8>>, function => <<"insert"/utf8>>, line => 336}) end, gleam@option:map( erlang:element(3, Registrant), fun(Tag) -> _assert_subject@1 = ets:insert( erlang:element(4, State), {Tag, Pid, erlang:element(2, Registrant)} ), true = case _assert_subject@1 of true -> _assert_subject@1; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@1, module => <<"chip"/utf8>>, function => <<"insert"/utf8>>, line => 339}) end end ), gleam@option:map( erlang:element(4, Registrant), fun(Group) -> _assert_subject@2 = ets:insert( erlang:element(5, State), {Group, Pid, erlang:element(2, Registrant)} ), true = case _assert_subject@2 of true -> _assert_subject@2; _assert_fail@2 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@2, module => <<"chip"/utf8>>, function => <<"insert"/utf8>>, line => 343}) end end ), nil. -spec find(gleam@erlang@process:subject(message(HPL, any(), any())), any()) -> {ok, gleam@erlang@process:subject(HPL)} | {error, nil}. find(Registry, Tag) -> Table = gleam@erlang@process:call( Registry, fun(_capture) -> {find, _capture} end, 500 ), case ets:lookup(Table, Tag) of [{_, _, Subject}] -> {ok, Subject}; [] -> {error, nil}; _ -> erlang:error(#{gleam_error => panic, message => <<"Impossible lookup on a tagged table."/utf8>>, module => <<"chip"/utf8>>, function => <<"find"/utf8>>, line => 137}) end. -spec delete( state(any(), any(), any()), gleam@erlang:reference_(), gleam@erlang@process:pid_() ) -> nil. delete(State, Monitor, Pid) -> nil = chip_erlang_ffi:demonitor(Monitor), _assert_subject = ets:match_delete( erlang:element(3, State), {match_any(), Pid} ), true = case _assert_subject of true -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"chip"/utf8>>, function => <<"delete"/utf8>>, line => 357}) end, _assert_subject@1 = ets:match_delete( erlang:element(4, State), {match_any(), Pid, match_any()} ), true = case _assert_subject@1 of true -> _assert_subject@1; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@1, module => <<"chip"/utf8>>, function => <<"delete"/utf8>>, line => 358}) end, _assert_subject@2 = ets:match_delete( erlang:element(5, State), {match_any(), Pid, match_any()} ), true = case _assert_subject@2 of true -> _assert_subject@2; _assert_fail@2 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@2, module => <<"chip"/utf8>>, function => <<"delete"/utf8>>, line => 360}) end, nil. -spec init() -> gleam@otp@actor:init_result(state(HQP, HQQ, HQR), message(HQP, HQQ, HQR)). init() -> Process_down = fun(Message) -> case chip_erlang_ffi:decode_down_message(Message) of {ok, {process_down, Monitor, Pid}} -> {demonitor, Monitor, Pid}; {error, nil} -> {no_operation, Message} end end, {ready, {state, chip_erlang_ffi:schedulers(), ets:new(chip_registry, [protected, set]), ets:new(chip_registry_tagged, [protected, set]), ets:new(chip_registry_grouped, [protected, bag])}, begin _pipe = gleam_erlang_ffi:new_selector(), gleam@erlang@process:selecting_anything(_pipe, Process_down) end}. -spec continue_dispatch( step(), fun((any()) -> gleam@erlang@process:subject(HSM)), fun((gleam@erlang@process:subject(HSM)) -> nil) ) -> nil. continue_dispatch(Step, Decode_record, Task) -> _pipe = chip_erlang_ffi:search(Step), handle_dispatch_results(_pipe, Decode_record, Task). -spec handle_dispatch_results( search(HSP), fun((HSP) -> gleam@erlang@process:subject(HSR)), fun((gleam@erlang@process:subject(HSR)) -> nil) ) -> nil. handle_dispatch_results(Lookup, Decode_record, Task) -> case Lookup of {partial, Objects, Step} -> _pipe = Objects, _pipe@1 = gleam@list:map(_pipe, Decode_record), run_batch(_pipe@1, Task), continue_dispatch(Step, Decode_record, Task); {end_of_table, Objects@1} -> _pipe@2 = Objects@1, _pipe@3 = gleam@list:map(_pipe@2, Decode_record), run_batch(_pipe@3, Task) end. -spec start_dispatch( gleam@erlang:reference_(), any(), fun((any()) -> gleam@erlang@process:subject(HSI)), fun((gleam@erlang@process:subject(HSI)) -> nil), integer() ) -> nil. start_dispatch(Table, Pattern, Decode_record, Task, Concurrency) -> gleam@erlang@process:start(fun() -> _pipe = Table, _pipe@1 = chip_erlang_ffi:search(_pipe, Pattern, Concurrency), handle_dispatch_results(_pipe@1, Decode_record, Task) end, false), nil. -spec loop(message(HRA, HRB, HRC), state(HRA, HRB, HRC)) -> gleam@otp@actor:next(message(HRA, HRB, HRC), state(HRA, HRB, HRC)). loop(Message, State) -> case Message of {register, Registrant} -> nil = insert(State, Registrant), {continue, State, none}; {demonitor, Monitor, Pid} -> nil = delete(State, Monitor, Pid), {continue, State, none}; {find, Client} -> gleam@erlang@process:send(Client, erlang:element(4, State)), {continue, State, none}; {dispatch, Callback} -> Match = {match_into(1), match_any()}, Cast = fun(Object) -> [Subject] = case Object of [_] -> Object; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"chip"/utf8>>, function => <<"loop"/utf8>>, line => 281}) end, Subject end, start_dispatch( erlang:element(3, State), Match, Cast, Callback, erlang:element(2, State) ), {continue, State, none}; {dispatch_group, Callback@1, Group} -> Match@1 = {Group, match_any(), match_into(1)}, Cast@1 = fun(Object@1) -> [Subject@1] = case Object@1 of [_] -> Object@1; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@1, module => <<"chip"/utf8>>, function => <<"loop"/utf8>>, line => 299}) end, Subject@1 end, start_dispatch( erlang:element(5, State), Match@1, Cast@1, Callback@1, erlang:element(2, State) ), {continue, State, none}; {no_operation, Message@1} -> gleam@io:println( <<"chip: received an out of bound message from a non-selected process.\n"/utf8, (gleam@string:inspect(Message@1))/binary>> ), {continue, State, none}; stop -> {stop, normal} 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}).