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).
17 , get/3 % get existing or default
18 , get/4 % get existing if valid, or default
29 , find_unique_presence_violations/2 % No optional keys
30 , find_unique_presence_violations/3 % Specify optional keys
31 , validate_unique_presence/2 % No optional keys
32 , validate_unique_presence/3 % Specify optional keys
33 , presence_violations_to_list/1
40 -type presence_violations(A) ::
41 % This is a hack to effectively parametarize the types of record fields.
42 % IMPORTANT: Make sure that the order of fields matches the definition of
43 % #hope_kv_list_presence_violations
44 { hope_kv_list_presence_violations
46 , [A] % keys_duplicated
47 , [A] % keys_unsupported
50 -type presence_error(A) ::
52 | {keys_duplicated , [A]}
53 | {keys_unsupported , [A]}
57 %% ============================================================================
59 %% ============================================================================
66 -spec get(t(K, V), K) ->
69 case lists:keyfind(K, 1, T)
74 -spec get(t(K, V), K, V) ->
78 hope_option:get(Vopt, Default).
80 -spec get(t(K, V), K, V, fun((V) -> boolean())) ->
82 get(T, K, Default, IsValid) ->
84 VOpt2 = hope_option:validate(VOpt1, IsValid),
85 hope_option:get(VOpt2, Default).
87 -spec set(t(K, V), K, V) ->
90 lists:keystore(K, 1, T, {K, V}).
92 -spec update(t(K, V), K, fun((hope_option:t(V)) -> V)) ->
97 % TODO: Eliminate the 2nd lookup.
100 -spec pop(t(K, V), K) ->
101 {hope_option:t(V), t(K, V)}.
103 case lists:keytake(K, 1, T1)
104 of {value, {K, V}, T2} -> {{some, V}, T2}
105 ; false -> {none , T1}
108 -spec iter(t(K, V), fun((K, V) -> any())) ->
112 ok = lists:foreach(F2, T),
115 -spec map(t(K, V), fun((K, V) -> V)) ->
118 F2 = fun ({K, _}=X) -> {K, apply_map(F1, X)} end,
121 -spec filter(t(K, V), fun((K, V) -> boolean())) ->
127 -spec fold(t(K, V), fun((K, V, Acc) -> Acc), Acc) ->
129 fold(T, F1, Accumulator) ->
130 F2 = fun ({K, V}, Acc) -> F1(K, V, Acc) end,
131 lists:foldl(F2, Accumulator, T).
133 -spec to_kv_list(t(K, V)) ->
138 -spec of_kv_list([{K, V}]) ->
141 % TODO: Decide if validation is to be done here. Do so if yes.
144 -spec validate_unique_presence(T, [K]) ->
145 hope_result:t(T, [presence_error(K)])
147 validate_unique_presence(T, KeysRequired) ->
149 validate_unique_presence(T, KeysRequired, KeysOptional).
151 -spec validate_unique_presence(t(K, _V), [K], [K]) ->
152 hope_result:t(T, [presence_error(K)])
154 validate_unique_presence(T, KeysRequired, KeysOptional) ->
155 case find_unique_presence_violations(T, KeysRequired, KeysOptional)
156 of #hope_kv_list_presence_violations
158 , keys_duplicated = []
159 , keys_unsupported = []
162 ; #hope_kv_list_presence_violations{}=Violations ->
163 {error, presence_violations_to_list(Violations)}
166 -spec find_unique_presence_violations(t(K, _V), [K]) ->
167 presence_violations(K).
168 find_unique_presence_violations(T, KeysRequired) ->
170 find_unique_presence_violations(T, KeysRequired, KeysOptional).
172 -spec find_unique_presence_violations(t(K, _V), [K], [K]) ->
173 presence_violations(K).
174 find_unique_presence_violations(T, KeysRequired, KeysOptional) ->
175 KeysSupported = KeysRequired ++ KeysOptional,
176 KeysGiven = [K || {K, _V} <- T],
177 KeysGivenUnique = lists:usort(KeysGiven),
178 KeysDuplicated = lists:usort(KeysGiven -- KeysGivenUnique),
179 KeysMissing = KeysRequired -- KeysGivenUnique,
180 KeysUnsupported = KeysGivenUnique -- KeysSupported,
181 #hope_kv_list_presence_violations
182 { keys_missing = KeysMissing
183 , keys_duplicated = KeysDuplicated
184 , keys_unsupported = KeysUnsupported
187 -spec presence_violations_to_list(presence_violations(K)) ->
189 presence_violations_to_list(#hope_kv_list_presence_violations
190 { keys_missing = KeysMissing
191 , keys_duplicated = KeysDuplicated
192 , keys_unsupported = KeysUnsupported
197 ; [_|_] -> [{keys_missing, KeysMissing}]
202 ; [_|_] -> [{keys_duplicated, KeysDuplicated}]
207 ; [_|_] -> [{keys_unsupported, KeysUnsupported}]
209 ErrorDups ++ ErrorMissing ++ ErrorUnsupported.
211 -spec has_key(t(K, _), K) ->
214 lists:any(fun ({K2, _}) -> K1 =:= K2 end, T).
216 %% ============================================================================
218 %% ============================================================================
222 when F :: fun(( K, V1 ) -> V2)
223 , G :: fun(({K, V1}) -> V2)
226 fun (X) -> apply_map(F, X) end.
228 -spec apply_map(fun((K, V1) -> V2), {K, V1}) ->
230 apply_map(F, {K, V}) ->