Ignore outputs of side-effecting, element-handler functions.
[hope.git] / src / hope_kv_list.erl
index f4ee72e..44a78d4 100644 (file)
@@ -3,7 +3,9 @@
 %%%----------------------------------------------------------------------------
 -module(hope_kv_list).
 
--behavior(hope_dictionary).
+-include_lib("hope_kv_list.hrl").
+
+-behavior(hope_gen_dictionary).
 
 -export_type(
     [ t/2
@@ -11,7 +13,9 @@
 
 -export(
     [ empty/0
-    , get/2
+    , get/2  % get option
+    , get/3  % get existing or default
+    , get/4  % get existing if valid, or default
     , set/3
     , update/3
     , pop/2
     , fold/3
     , of_kv_list/1
     , to_kv_list/1
-    , validate_unique_presence/2  % Assume default optional parameter(s)
-    , validate_unique_presence/3  % Specify optional parameter(s)
+    , has_key/2
+    , find_unique_presence_violations/2  % No optional keys
+    , find_unique_presence_violations/3  % Specify optional keys
+    , validate_unique_presence/2  % No optional keys
+    , validate_unique_presence/3  % Specify optional keys
+    , presence_violations_to_list/1
     ]).
 
 
 -type t(K, V) ::
     [{K, V}].
 
+-type presence_violations(A) ::
+    % This is a hack to effectively parametarize the types of record fields.
+    % IMPORTANT: Make sure that the order of fields matches the definition of
+    % #hope_kv_list_presence_violations
+    { hope_kv_list_presence_violations
+    , [A]  % keys_missing
+    , [A]  % keys_duplicated
+    , [A]  % keys_unsupported
+    }.
+
 -type presence_error(A) ::
       {keys_missing     , [A]}
     | {keys_duplicated  , [A]}
@@ -53,6 +71,19 @@ get(T, K) ->
     ;   {K, V} -> {some, V}
     end.
 
+-spec get(t(K, V), K, V) ->
+    V.
+get(T, K, Default) ->
+    Vopt = get(T, K),
+    hope_option:get(Vopt, Default).
+
+-spec get(t(K, V), K, V, fun((V) -> boolean())) ->
+    V.
+get(T, K, Default, IsValid) ->
+    VOpt1 = get(T, K),
+    VOpt2 = hope_option:validate(VOpt1, IsValid),
+    hope_option:get(VOpt2, Default).
+
 -spec set(t(K, V), K, V) ->
     t(K, V).
 set(T, K, V) ->
@@ -74,7 +105,7 @@ pop(T1, K) ->
     ;   false               -> {none     , T1}
     end.
 
--spec iter(t(K, V), fun((K, V) -> ok)) ->
+-spec iter(t(K, V), fun((K, V) -> any())) ->
     ok.
 iter(T, F1) ->
     F2 = lift_map(F1),
@@ -109,44 +140,77 @@ of_kv_list(List) ->
     % TODO: Decide if validation is to be done here. Do so if yes.
     List.
 
--spec validate_unique_presence(t(K, _V), [K]) ->
-    hope_result:t(ok, [presence_error(K)]).
+-spec validate_unique_presence(T, [K]) ->
+    hope_result:t(T, [presence_error(K)])
+    when T :: t(K, _V).
 validate_unique_presence(T, KeysRequired) ->
     KeysOptional = [],
     validate_unique_presence(T, KeysRequired, KeysOptional).
 
 -spec validate_unique_presence(t(K, _V), [K], [K]) ->
-    hope_result:t(ok, [presence_error(K)]).
+    hope_result:t(T, [presence_error(K)])
+    when T :: t(K, _V).
 validate_unique_presence(T, KeysRequired, KeysOptional) ->
+    case find_unique_presence_violations(T, KeysRequired, KeysOptional)
+    of  #hope_kv_list_presence_violations
+        { keys_missing     = []
+        , keys_duplicated  = []
+        , keys_unsupported = []
+        } ->
+            {ok, T}
+    ;   #hope_kv_list_presence_violations{}=Violations ->
+            {error, presence_violations_to_list(Violations)}
+    end.
+
+-spec find_unique_presence_violations(t(K, _V), [K]) ->
+    presence_violations(K).
+find_unique_presence_violations(T, KeysRequired) ->
+    KeysOptional = [],
+    find_unique_presence_violations(T, KeysRequired, KeysOptional).
+
+-spec find_unique_presence_violations(t(K, _V), [K], [K]) ->
+    presence_violations(K).
+find_unique_presence_violations(T, KeysRequired, KeysOptional) ->
     KeysSupported   = KeysRequired ++ KeysOptional,
     KeysGiven       = [K || {K, _V} <- T],
     KeysGivenUnique = lists:usort(KeysGiven),
-    KeysDups        = lists:usort(KeysGiven -- KeysGivenUnique),
+    KeysDuplicated  = lists:usort(KeysGiven -- KeysGivenUnique),
     KeysMissing     = KeysRequired -- KeysGivenUnique,
     KeysUnsupported = KeysGivenUnique -- KeysSupported,
-    case {KeysDups, KeysMissing, KeysUnsupported}
-    of  {[], [], []} ->
-            {ok, ok}
-    ;   {Dups, Missing, Unsupported} ->
-            ErrorDups =
-                case Dups
-                of  []    -> []
-                ;   [_|_] -> [{keys_duplicated, Dups}]
-                end,
-            ErrorMissing =
-                case Missing
-                of  []    -> []
-                ;   [_|_] -> [{keys_missing, Missing}]
-                end,
-            ErrorUnsupported =
-                case Unsupported
-                of  []    -> []
-                ;   [_|_] -> [{keys_unsupported, Unsupported}]
-                end,
-            Errors = ErrorDups ++ ErrorMissing ++ ErrorUnsupported,
-            {error, Errors}
-    end.
-
+    #hope_kv_list_presence_violations
+    { keys_missing     = KeysMissing
+    , keys_duplicated  = KeysDuplicated
+    , keys_unsupported = KeysUnsupported
+    }.
+
+-spec presence_violations_to_list(presence_violations(K)) ->
+    [presence_error(K)].
+presence_violations_to_list(#hope_kv_list_presence_violations
+{ keys_missing     = KeysMissing
+, keys_duplicated  = KeysDuplicated
+, keys_unsupported = KeysUnsupported
+}) ->
+    ErrorMissing =
+        case KeysMissing
+        of  []    -> []
+        ;   [_|_] -> [{keys_missing, KeysMissing}]
+        end,
+    ErrorDups =
+        case KeysDuplicated
+        of  []    -> []
+        ;   [_|_] -> [{keys_duplicated, KeysDuplicated}]
+        end,
+    ErrorUnsupported =
+        case KeysUnsupported
+        of  []    -> []
+        ;   [_|_] -> [{keys_unsupported, KeysUnsupported}]
+        end,
+    ErrorDups ++ ErrorMissing ++ ErrorUnsupported.
+
+-spec has_key(t(K, _), K) ->
+    boolean().
+has_key(T, K1) ->
+    lists:any(fun ({K2, _}) -> K1 =:= K2 end, T).
 
 %% ============================================================================
 %% Helpers
This page took 0.023496 seconds and 4 git commands to generate.