src/erlang_ds_register.erl

-module(erlang_ds_register).

-dialyzer([
    {nowarn_function, bench/0},
    {nowarn_function, get_type_handlers_appvar/0}
]).

-export([
    get_type_handlers/0,
    register_type_handler/1,
    unregister_type_handler/1
]).

-export([
    get_updater/1,
    register_updater/2,
    register_updaters/1,
    unregister_updater/1,
    unregister_updaters/1
]).

-export([
    build_lookup/0
]).

%% only used for testing
-export([
    bench/0
]).

get_type_handlers() ->
    case erlang:function_exported(erlang_ds_lookup, type_handlers, 0) of
        true ->
            erlang_ds_lookup:type_handlers();
        false ->
            build_lookup(),
            erlang_ds_lookup:type_handlers()
    end.

get_type_handlers_appvar() ->
    application:get_env(erlang_ds, type_handlers, []).

%% To verify how much faster the module-based version is
%% On my machine, module-based lookup is 5-12x faster
bench() ->
    register_type_handler(a),
    register_type_handler(b),
    register_type_handler(c),

    Seq = lists:seq(1, 100000),
    {Timer1, Res1} = timer:tc(fun() ->
        [get_type_handlers() || _ <- Seq]
    end),

    {Timer2, Res2} = timer:tc(fun() ->
        [get_type_handlers_appvar() || _ <- Seq]
    end),
    io:format("PrintingResults:~n~p~nFrom Module: ~p.~nFrom App Var: ~p",[Res1 ++ Res2, Timer1, Timer2]).


register_type_handler(Mod) when is_atom(Mod) ->
    with_app_var(type_handlers, erlang_ds_builder:default_type_modules(), fun(Mods) ->
        case lists:member(Mod, Mods) of
            true ->
                Mods;
            false ->
                [Mod | Mods]
        end
    end),
    build_lookup().

unregister_type_handler(Mod) when is_atom(Mod) ->
    with_app_var(type_handlers, erlang_ds_builder:default_type_modules(), fun(Mods) ->
        Mods -- [Mod]
    end),
    build_lookup().



get_updater(UpdaterKey) ->
    case erlang:function_exported(erlang_ds_lookup, updater, 1) of
        true ->
            erlang_ds_lookup:updater(UpdaterKey);
        false ->
            build_lookup(),
            erlang_ds_lookup:updater(UpdaterKey)
    end.


register_updater(Key0, MFA0) ->
    {Key, MFA} = ds_util:normalize_updater_pair(Key0, MFA0),
    store_updater(Key, MFA).

register_updaters(KeyMFAs) ->
    Updaters = ds_util:normalize_updaters(KeyMFAs),
    store_updaters(Updaters).

      
store_updater(Key, MFA) ->
    with_app_var(updaters, erlang_ds_builder:default_updaters(), fun(Updaters) ->
        ds:set(Updaters, Key, MFA)
    end),
    build_lookup().

store_updaters(NewUpdaters) ->
    with_app_var(updaters, erlang_ds_builder:default_updaters(), fun(Updaters) ->
        ds:set(Updaters, NewUpdaters)
    end),
    build_lookup().

unregister_updater(Key0) ->
    Key = ds_util:normalize_updater_key(Key0),
    with_app_var(updaters, erlang_ds_builder:default_updaters(), fun(Updaters) ->
        ds:delete(Updaters, Key)
    end),
    build_lookup().

unregister_updaters(Keys0) ->
    Keys = [ds_util:normalize_updater_key(K) || K <- Keys0],
    with_app_var(updaters, erlang_ds_builder:default_updaters(), fun(Updaters) ->
        ds:delete_list(Updaters, Keys)
    end),
    build_lookup().

build_lookup() ->
    erlang_ds_builder:build().

with_app_var(Key, Default, Fun) ->
    Var = application:get_env(erlang_ds, Key, Default),
    NewVar = Fun(Var),
    application:set_env(erlang_ds, Key, NewVar).