1 %%%----------------------------------------------------------------------------
2 %%% Equivalent to stdlib's orddict, but with a pretty (IMO), uniform interface.
3 %%%----------------------------------------------------------------------------
6 -include_lib("hope_kv_list.hrl").
8 -behavior(hope_gen_dictionary).
27 , find_unique_presence_violations/2 % No optional keys
28 , find_unique_presence_violations/3 % Specify optional keys
29 , validate_unique_presence/2 % No optional keys
30 , validate_unique_presence/3 % Specify optional keys
37 -type presence_violations(A) ::
38 % This is a hack to effectively parametarize the types of record fields.
39 % IMPORTANT: Make sure that the order of fields matches the definition of
40 % #hope_kv_list_presence_violations
41 { hope_kv_list_presence_violations
43 , [A] % keys_duplicated
44 , [A] % keys_unsupported
47 -type presence_error(A) ::
49 | {keys_duplicated , [A]}
50 | {keys_unsupported , [A]}
54 %% ============================================================================
56 %% ============================================================================
63 -spec get(t(K, V), K) ->
66 case lists:keyfind(K, 1, T)
71 -spec get(t(K, V), K, V) ->
75 hope_option:get(Vopt, Default).
77 -spec set(t(K, V), K, V) ->
80 lists:keystore(K, 1, T, {K, V}).
82 -spec update(t(K, V), K, fun((hope_option:t(V)) -> V)) ->
87 % TODO: Eliminate the 2nd lookup.
90 -spec pop(t(K, V), K) ->
91 {hope_option:t(V), t(K, V)}.
93 case lists:keytake(K, 1, T1)
94 of {value, {K, V}, T2} -> {{some, V}, T2}
95 ; false -> {none , T1}
98 -spec iter(t(K, V), fun((K, V) -> ok)) ->
102 lists:foreach(F2, T).
104 -spec map(t(K, V), fun((K, V) -> V)) ->
107 F2 = fun ({K, _}=X) -> {K, apply_map(F1, X)} end,
110 -spec filter(t(K, V), fun((K, V) -> boolean())) ->
116 -spec fold(t(K, V), fun((K, V, Acc) -> Acc), Acc) ->
118 fold(T, F1, Accumulator) ->
119 F2 = fun ({K, V}, Acc) -> F1(K, V, Acc) end,
120 lists:foldl(F2, Accumulator, T).
122 -spec to_kv_list(t(K, V)) ->
127 -spec of_kv_list([{K, V}]) ->
130 % TODO: Decide if validation is to be done here. Do so if yes.
133 -spec validate_unique_presence(t(K, _V), [K]) ->
134 hope_result:t(ok, [presence_error(K)]).
135 validate_unique_presence(T, KeysRequired) ->
137 validate_unique_presence(T, KeysRequired, KeysOptional).
139 -spec validate_unique_presence(t(K, _V), [K], [K]) ->
140 hope_result:t(ok, [presence_error(K)]).
141 validate_unique_presence(T, KeysRequired, KeysOptional) ->
142 case find_unique_presence_violations(T, KeysRequired, KeysOptional)
143 of #hope_kv_list_presence_violations
145 , keys_duplicated = []
146 , keys_unsupported = []
149 ; #hope_kv_list_presence_violations
150 { keys_missing = KeysMissing
151 , keys_duplicated = KeysDuplicated
152 , keys_unsupported = KeysUnsupported
157 ; [_|_] -> [{keys_missing, KeysMissing}]
162 ; [_|_] -> [{keys_duplicated, KeysDuplicated}]
167 ; [_|_] -> [{keys_unsupported, KeysUnsupported}]
169 Errors = ErrorDups ++ ErrorMissing ++ ErrorUnsupported,
173 -spec find_unique_presence_violations(t(K, _V), [K]) ->
174 presence_violations(K).
175 find_unique_presence_violations(T, KeysRequired) ->
177 find_unique_presence_violations(T, KeysRequired, KeysOptional).
179 -spec find_unique_presence_violations(t(K, _V), [K], [K]) ->
180 presence_violations(K).
181 find_unique_presence_violations(T, KeysRequired, KeysOptional) ->
182 KeysSupported = KeysRequired ++ KeysOptional,
183 KeysGiven = [K || {K, _V} <- T],
184 KeysGivenUnique = lists:usort(KeysGiven),
185 KeysDuplicated = lists:usort(KeysGiven -- KeysGivenUnique),
186 KeysMissing = KeysRequired -- KeysGivenUnique,
187 KeysUnsupported = KeysGivenUnique -- KeysSupported,
188 #hope_kv_list_presence_violations
189 { keys_missing = KeysMissing
190 , keys_duplicated = KeysDuplicated
191 , keys_unsupported = KeysUnsupported
195 %% ============================================================================
197 %% ============================================================================
201 when F :: fun(( K, V1 ) -> V2)
202 , G :: fun(({K, V1}) -> V2)
205 fun (X) -> apply_map(F, X) end.
207 -spec apply_map(fun((K, V1) -> V2), {K, V1}) ->
209 apply_map(F, {K, V}) ->