Current section
Files
Jump to
Current section
Files
src/macula_rpc_registry.erl
%%%-------------------------------------------------------------------
%%% @doc
%%% Local RPC procedure registration registry.
%%% Maps URIs to handler functions.
%%% Supports multiple handlers per URI (for load balancing).
%%% @end
%%%-------------------------------------------------------------------
-module(macula_rpc_registry).
%% API
-export([
new/0,
new/1,
register/4,
unregister/3,
find/2,
find_handlers/2,
list_uris/1,
list_registrations/1,
size/1
]).
%% Types
-type handler_fn() :: fun((map()) -> {ok, term()} | {error, term()}).
-type registration() :: #{
uri := binary(),
handler := handler_fn(),
metadata := map()
}.
-type invocation_strategy() :: round_robin | random | local_first.
-type registry() :: #{
registrations := [registration()],
strategy := invocation_strategy()
}.
-export_type([
handler_fn/0,
registration/0,
registry/0,
invocation_strategy/0
]).
%%%===================================================================
%%% API Functions
%%%===================================================================
%% @doc Create new empty registry with default strategy (round_robin).
-spec new() -> registry().
new() ->
new(round_robin).
%% @doc Create new empty registry with custom strategy.
-spec new(invocation_strategy()) -> registry().
new(Strategy) ->
#{
registrations => [],
strategy => Strategy
}.
%% @doc Register a procedure handler.
%% Allows multiple handlers for the same URI (for load balancing).
-spec register(registry(), binary(), handler_fn(), map()) -> registry().
register(#{registrations := Regs} = Registry, Uri, Handler, Metadata) ->
Registration = #{
uri => Uri,
handler => Handler,
metadata => Metadata
},
NewRegs = [Registration | Regs],
Registry#{registrations => NewRegs}.
%% @doc Unregister a specific procedure handler.
%% Only removes the exact handler function.
-spec unregister(registry(), binary(), handler_fn()) -> registry().
unregister(#{registrations := Regs} = Registry, Uri, Handler) ->
NewRegs = lists:filter(
fun(Reg) ->
not (maps:get(uri, Reg) =:= Uri andalso
maps:get(handler, Reg) =:= Handler)
end,
Regs
),
Registry#{registrations => NewRegs}.
%% @doc Find all registrations for a URI.
-spec find(registry(), binary()) -> [registration()].
find(#{registrations := Regs}, Uri) ->
lists:filter(
fun(Reg) -> maps:get(uri, Reg) =:= Uri end,
Regs
).
%% @doc Find all handlers for a URI.
-spec find_handlers(registry(), binary()) -> {ok, [registration()]} | not_found.
find_handlers(#{registrations := Regs}, Uri) ->
Matches = lists:filter(
fun(Reg) -> maps:get(uri, Reg) =:= Uri end,
Regs
),
case Matches of
[] -> not_found;
_ -> {ok, Matches}
end.
%% @doc List all unique URIs in registry.
-spec list_uris(registry()) -> [binary()].
list_uris(#{registrations := Regs}) ->
Uris = lists:map(
fun(Reg) -> maps:get(uri, Reg) end,
Regs
),
lists:usort(Uris).
%% @doc List all registrations.
-spec list_registrations(registry()) -> [registration()].
list_registrations(#{registrations := Regs}) ->
Regs.
%% @doc Get total number of registrations.
-spec size(registry()) -> non_neg_integer().
size(#{registrations := Regs}) ->
length(Regs).