Current section

Files

Jump to
evoq src evoq_type_provider.erl
Raw

src/evoq_type_provider.erl

%% @doc Type provider for event type to module mapping.
%%
%% Maintains mappings between:
%% - Event types (binary strings) and event modules
%% - Event types and their upcasters
%%
%% This enables:
%% - Dynamic event deserialization
%% - Schema evolution through upcasters
%% - Type-safe event handling
%%
%% @author rgfaber
-module(evoq_type_provider).
-behaviour(gen_server).
-include("evoq.hrl").
%% API
-export([start_link/0]).
-export([register_event/2, register_upcaster/2]).
-export([get_module/1, get_upcaster/1]).
-export([get_all_types/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
-define(SERVER, ?MODULE).
-define(TABLE_EVENTS, evoq_type_provider_events).
-define(TABLE_UPCASTERS, evoq_type_provider_upcasters).
-record(state, {}).
%%====================================================================
%% API
%%====================================================================
%% @doc Start the type provider.
-spec start_link() -> {ok, pid()} | {error, term()}.
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%% @doc Register an event type to module mapping.
-spec register_event(binary(), atom()) -> ok.
register_event(EventType, Module) ->
gen_server:call(?SERVER, {register_event, EventType, Module}).
%% @doc Register an upcaster for an event type.
-spec register_upcaster(binary(), atom()) -> ok.
register_upcaster(EventType, UpcasterModule) ->
gen_server:call(?SERVER, {register_upcaster, EventType, UpcasterModule}).
%% @doc Get the module for an event type.
-spec get_module(binary()) -> {ok, atom()} | {error, not_found}.
get_module(EventType) ->
case ets:lookup(?TABLE_EVENTS, EventType) of
[{EventType, Module}] -> {ok, Module};
[] -> {error, not_found}
end.
%% @doc Get the upcaster for an event type.
-spec get_upcaster(binary()) -> {ok, atom()} | {error, not_found}.
get_upcaster(EventType) ->
case ets:lookup(?TABLE_UPCASTERS, EventType) of
[{EventType, Upcaster}] -> {ok, Upcaster};
[] -> {error, not_found}
end.
%% @doc Get all registered event types.
-spec get_all_types() -> [binary()].
get_all_types() ->
[Type || {Type, _} <- ets:tab2list(?TABLE_EVENTS)].
%%====================================================================
%% gen_server callbacks
%%====================================================================
%% @private
init([]) ->
%% Create ETS tables for fast lookup
_ = create_table(?TABLE_EVENTS),
_ = create_table(?TABLE_UPCASTERS),
{ok, #state{}}.
%% @private
handle_call({register_event, EventType, Module}, _From, State) ->
ets:insert(?TABLE_EVENTS, {EventType, Module}),
{reply, ok, State};
handle_call({register_upcaster, EventType, UpcasterModule}, _From, State) ->
ets:insert(?TABLE_UPCASTERS, {EventType, UpcasterModule}),
{reply, ok, State};
handle_call(_Request, _From, State) ->
{reply, {error, unknown_request}, State}.
%% @private
handle_cast(_Msg, State) ->
{noreply, State}.
%% @private
handle_info(_Info, State) ->
{noreply, State}.
%% @private
terminate(_Reason, _State) ->
ok.
%%====================================================================
%% Internal functions
%%====================================================================
%% @private
create_table(Name) ->
case ets:whereis(Name) of
undefined ->
ets:new(Name, [
named_table,
public,
set,
{read_concurrency, true}
]);
_Tid ->
Name
end.