Packages

Erlang Standard Context

Current section

Files

Jump to
context src context.erl
Raw

src/context.erl

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% @doc Erlang standard Context
%%% @end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%_* Module declaration ===============================================
-module(context).
-compile({no_auto_import,[size/1]}).
%%%_* Exports ==========================================================
%%%_ * API -------------------------------------------------------------
-export([new/0]).
%% Basics
-export([del/2]).
-export([equal/2]).
-export([get/2]).
-export([get/3]).
-export([get_/2]).
-export([is_empty/1]).
-export([is_key/2]).
-export([keys/1]).
-export([set/3]).
-export([size/1]).
-export([is_context/1]).
-export_type([context/0]).
%%%_* Code =============================================================
%%%_* Types ============================================================
-record(c, {obj :: type()}).
-opaque context() :: #c{}.
-type ctx() :: context().
-type type() :: map().
%%%_* Macros ===========================================================
-define(tag(Obj), #c{obj=Obj}).
%%%_ * API -------------------------------------------------------------
%% Returns true if `Term` is a context
-spec is_context(any()) -> boolean().
is_context(#c{}) -> true;
is_context(_) -> false.
%% Create a new context
-spec new() -> ctx().
new() -> ?tag(maps:new()).
%% Removes the key, if it exists, and its associated value from context
%% and returns a new context without key.
-spec del(ctx(), term()) -> ctx().
del(#c{obj=O}, K) -> ?tag(maps:remove(K, O)).
%% Returns true if ctx and ctx are identical
-spec equal(ctx(), ctx()) -> boolean().
equal(#c{obj=O}, #c{obj=O}) -> true;
equal(#c{obj=O}, #c{obj=O2}) ->
lists:sort(maps:to_list(O)) =:= lists:sort(maps:to_list(O2)).
%% Get the value associated with the key form the context
-spec get(ctx(), term()) -> {ok, term()} | {error, notfound}.
get(#c{obj=O}, K) -> maybe_get(O, K).
%% Get the value associated with the key from the context.
%% If key is not found return default value.
-spec get(ctx(), term(), term()) -> term().
get(#c{obj=O}, K, D) -> maybe_get(O, K, D).
%% Same as `get/2` but return the value without tag(`{ok, Val}`).
-spec get_(ctx(), term()) -> term().
get_(#c{obj=O}, K) -> maybe_get_(O, K).
%% Returns true if ctx is empty.
-spec is_empty(ctx()) -> boolean().
is_empty(Ctx) -> 0 =:= size(Ctx).
%% Returns true if key exist in context.
-spec is_key(ctx(), term()) -> boolean().
is_key(#c{obj=O}, K) -> maps:is_key(K, O).
%% Returns all keys associated with context as a list.
-spec keys(ctx()) -> list(term()).
keys(#c{obj=O}) -> maps:keys(O).
%% Upsert the value V for the key K.
-spec set(ctx(), term(), term()) -> ctx().
set(#c{obj=O}, K, V) -> ?tag(maps:put(K, V, O)).
%% Return the number of entries in the context.
-spec size(ctx()) -> non_neg_integer().
size(#c{obj=O}) -> maps:size(O).
%%%_* Private functions ================================================
maybe_get(O, K) ->
case maps:get(K, O, {error, notfound}) of
{error, notfound} -> {error, notfound};
Val -> {ok, Val}
end.
maybe_get_(O, K) ->
maybe_get(O, K, {error, notfound}).
maybe_get(O, K, D) ->
case maybe_get(O, K) of
{error, notfound} -> D;
{ok, Val} -> Val
end.
%%%_* Tests ============================================================
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
new_test() ->
?assertEqual(?tag(maps:new()), new()).
del_test() ->
?assertEqual(new(), del(set(new(), key, val), key)),
?assertEqual(set(new(), key, val), del(set(new(), key, val), bla)).
equal_test() ->
?assertEqual(true, equal(new(), new())),
?assertEqual(false, equal(new(), set(new(), k, v))).
get_test() ->
?assertEqual({ok, b}, get(set(new(), a, b), a)),
?assertEqual({error, notfound}, get(set(new(), a, b), b)),
?assertEqual(b, get_(set(new(), a, b), a)),
?assertEqual({error, notfound}, get_(set(new(), a, b), b)),
?assertEqual(b, get(set(new(), a, b), a, c)),
?assertEqual(c, get(new(), b, c)).
is_empty_test() ->
?assertEqual(true, is_empty(new())),
?assertEqual(false, is_empty(set(new(), a, b))).
is_key_test() ->
?assertEqual(false, is_key(new(), a)),
?assertEqual(true, is_key(set(new(), a, b), a)).
keys_test() ->
?assertEqual([], keys(new())),
?assertEqual([a,b], keys(set(set(new(), a, a), b, b))).
size_test() ->
?assertEqual(0, size(new())),
?assertEqual(2, size(set(set(new(), a, a), b, b))).
-endif.
%%%_* Emacs ============================================================
%%% Local Variables:
%%% allout-layout: t
%%% erlang-indent-level: 2
%%% End