Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 27s
121 lines
3.9 KiB
Erlang
121 lines
3.9 KiB
Erlang
-module(registry).
|
|
-behaviour(gen_server).
|
|
-export([new/0, kinds/0, register/4, lookup/3, list/2]).
|
|
-export([start_link/0, register/3, lookup/2, list/1, stop/0]).
|
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
|
|
|
|
%% Pure-functional registry for the seven bootstrap kinds.
|
|
%%
|
|
%% State is a property list keyed by kind atom; each kind's value
|
|
%% is itself a property list of {Name, Entry} pairs. Entry is
|
|
%% opaque — typically a proplist with :cid, :schema, :semantics,
|
|
%% :supersedes fields, but the registry doesn't enforce that here.
|
|
%%
|
|
%% A gen_server wrapper (Step 5b) will own the global registry
|
|
%% process; the pure functions in this module remain the canonical
|
|
%% API and are usable for tests and for offline projection-replay.
|
|
%%
|
|
%% Return shapes:
|
|
%% new/0 -> State
|
|
%% kinds/0 -> [Atom, ...]
|
|
%% register/4 -> {ok, NewState} | {error, unknown_kind}
|
|
%% lookup/3 -> {ok, Entry} | not_found | {error, unknown_kind}
|
|
%% list/2 -> [{Name, Entry}, ...] | {error, unknown_kind}
|
|
|
|
new() -> [].
|
|
|
|
kinds() ->
|
|
[activity_types, object_types, projections,
|
|
validators, codecs, sig_suites, audience].
|
|
|
|
register(Kind, Name, Entry, State) ->
|
|
case is_valid_kind(Kind) of
|
|
false -> {error, unknown_kind};
|
|
true ->
|
|
Entries = kind_entries(Kind, State),
|
|
Updated = put_pair(Name, Entry, Entries),
|
|
{ok, set_kind_entries(Kind, Updated, State)}
|
|
end.
|
|
|
|
lookup(Kind, Name, State) ->
|
|
case is_valid_kind(Kind) of
|
|
false -> {error, unknown_kind};
|
|
true ->
|
|
find_pair(Name, kind_entries(Kind, State))
|
|
end.
|
|
|
|
list(Kind, State) ->
|
|
case is_valid_kind(Kind) of
|
|
false -> {error, unknown_kind};
|
|
true -> kind_entries(Kind, State)
|
|
end.
|
|
|
|
%% ── Internal ────────────────────────────────────────────────────
|
|
|
|
is_valid_kind(K) -> lists:member(K, kinds()).
|
|
|
|
kind_entries(Kind, State) ->
|
|
case find_pair(Kind, State) of
|
|
not_found -> [];
|
|
{ok, V} -> V
|
|
end.
|
|
|
|
set_kind_entries(Kind, Entries, State) ->
|
|
put_pair(Kind, Entries, State).
|
|
|
|
put_pair(K, V, []) -> [{K, V}];
|
|
put_pair(K, V, [{K, _} | Rest]) -> [{K, V} | Rest];
|
|
put_pair(K, V, [P | Rest]) -> [P | put_pair(K, V, Rest)].
|
|
|
|
find_pair(_, []) -> not_found;
|
|
find_pair(K, [{K, V} | _]) -> {ok, V};
|
|
find_pair(K, [_ | Rest]) -> find_pair(K, Rest).
|
|
|
|
%% ── Step 5b: gen_server wrapper ─────────────────────────────────
|
|
%%
|
|
%% The named process owns the registry state; concurrent readers
|
|
%% and writers serialize through gen_server:call. The pure /3 and
|
|
%% /4 functions remain available for offline projection-replay and
|
|
%% for tests that don't need a process at all.
|
|
%%
|
|
%% Port notes: gen_server:start_link returns the raw Pid (not
|
|
%% `{ok, Pid}` as in OTP). `?MODULE` macro is unsupported here, so
|
|
%% the registered name is the literal `registry` atom in every call.
|
|
|
|
start_link() ->
|
|
Pid = gen_server:start_link(registry, []),
|
|
erlang:register(registry, Pid),
|
|
Pid.
|
|
|
|
stop() ->
|
|
R = gen_server:call(registry, '$gen_stop'),
|
|
erlang:unregister(registry),
|
|
R.
|
|
|
|
register(Kind, Name, Entry) ->
|
|
gen_server:call(registry, {register, Kind, Name, Entry}).
|
|
|
|
lookup(Kind, Name) ->
|
|
gen_server:call(registry, {lookup, Kind, Name}).
|
|
|
|
list(Kind) ->
|
|
gen_server:call(registry, {list, Kind}).
|
|
|
|
%% gen_server callbacks
|
|
|
|
init(_) -> {ok, new()}.
|
|
|
|
handle_call({register, Kind, Name, Entry}, _From, State) ->
|
|
case register(Kind, Name, Entry, State) of
|
|
{ok, NewState} -> {reply, ok, NewState};
|
|
{error, R} -> {reply, {error, R}, State}
|
|
end;
|
|
handle_call({lookup, Kind, Name}, _From, State) ->
|
|
{reply, lookup(Kind, Name, State), State};
|
|
handle_call({list, Kind}, _From, State) ->
|
|
{reply, list(Kind, State), State}.
|
|
|
|
handle_cast(_, S) -> {noreply, S}.
|
|
|
|
handle_info(_, S) -> {noreply, S}.
|