Return k/v list itself from validate_unique_presence.
[hope.git] / src / hope_kv_list.erl
CommitLineData
03ac148f
SK
1%%%----------------------------------------------------------------------------
2%%% Equivalent to stdlib's orddict, but with a pretty (IMO), uniform interface.
3%%%----------------------------------------------------------------------------
4-module(hope_kv_list).
5
e163be89
SK
6-include_lib("hope_kv_list.hrl").
7
465f1bde 8-behavior(hope_gen_dictionary).
03ac148f
SK
9
10-export_type(
11 [ t/2
12 ]).
13
14-export(
15 [ empty/0
16 , get/2
870172d6 17 , get/3
03ac148f
SK
18 , set/3
19 , update/3
70cf8e86 20 , pop/2
03ac148f
SK
21 , iter/2
22 , map/2
23 , filter/2
24 , fold/3
25 , of_kv_list/1
26 , to_kv_list/1
e163be89
SK
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
dfcd229c 31 , presence_violations_to_list/1
03ac148f
SK
32 ]).
33
34
35-type t(K, V) ::
36 [{K, V}].
37
e163be89
SK
38-type presence_violations(A) ::
39 % This is a hack to effectively parametarize the types of record fields.
40 % IMPORTANT: Make sure that the order of fields matches the definition of
41 % #hope_kv_list_presence_violations
42 { hope_kv_list_presence_violations
43 , [A] % keys_missing
44 , [A] % keys_duplicated
45 , [A] % keys_unsupported
46 }.
47
fa24061d
SK
48-type presence_error(A) ::
49 {keys_missing , [A]}
50 | {keys_duplicated , [A]}
51 | {keys_unsupported , [A]}
52 .
53
03ac148f
SK
54
55%% ============================================================================
56%% API
57%% ============================================================================
58
59-spec empty() ->
60 [].
61empty() ->
62 [].
63
95c3c2a7
PO
64-spec get(t(K, V), K) ->
65 hope_option:t(V).
03ac148f
SK
66get(T, K) ->
67 case lists:keyfind(K, 1, T)
68 of false -> none
69 ; {K, V} -> {some, V}
70 end.
71
870172d6
SK
72-spec get(t(K, V), K, V) ->
73 V.
74get(T, K, Default) ->
75 Vopt = get(T, K),
76 hope_option:get(Vopt, Default).
77
95c3c2a7
PO
78-spec set(t(K, V), K, V) ->
79 t(K, V).
03ac148f
SK
80set(T, K, V) ->
81 lists:keystore(K, 1, T, {K, V}).
82
95c3c2a7
PO
83-spec update(t(K, V), K, fun((hope_option:t(V)) -> V)) ->
84 t(K, V).
03ac148f
SK
85update(T, K, F) ->
86 V1Opt = get(T, K),
87 V2 = F(V1Opt),
37c6e98b 88 % TODO: Eliminate the 2nd lookup.
03ac148f
SK
89 set(T, K, V2).
90
95c3c2a7
PO
91-spec pop(t(K, V), K) ->
92 {hope_option:t(V), t(K, V)}.
70cf8e86
SK
93pop(T1, K) ->
94 case lists:keytake(K, 1, T1)
95 of {value, {K, V}, T2} -> {{some, V}, T2}
96 ; false -> {none , T1}
97 end.
98
95c3c2a7
PO
99-spec iter(t(K, V), fun((K, V) -> ok)) ->
100 ok.
c1672ac4 101iter(T, F1) ->
d10156b0 102 F2 = lift_map(F1),
c1672ac4 103 lists:foreach(F2, T).
03ac148f 104
95c3c2a7
PO
105-spec map(t(K, V), fun((K, V) -> V)) ->
106 t(K, V).
c1672ac4 107map(T, F1) ->
d10156b0
SK
108 F2 = fun ({K, _}=X) -> {K, apply_map(F1, X)} end,
109 lists:map(F2, T).
03ac148f 110
95c3c2a7
PO
111-spec filter(t(K, V), fun((K, V) -> boolean())) ->
112 t(K, V).
c1672ac4 113filter(T, F1) ->
d10156b0 114 F2 = lift_map(F1),
c1672ac4 115 lists:filter(F2, T).
03ac148f 116
95c3c2a7
PO
117-spec fold(t(K, V), fun((K, V, Acc) -> Acc), Acc) ->
118 Acc.
03ac148f
SK
119fold(T, F1, Accumulator) ->
120 F2 = fun ({K, V}, Acc) -> F1(K, V, Acc) end,
097a2aa3 121 lists:foldl(F2, Accumulator, T).
03ac148f 122
95c3c2a7
PO
123-spec to_kv_list(t(K, V)) ->
124 [{K, V}].
03ac148f
SK
125to_kv_list(T) ->
126 T.
127
95c3c2a7
PO
128-spec of_kv_list([{K, V}]) ->
129 t(K, V).
03ac148f 130of_kv_list(List) ->
37c6e98b 131 % TODO: Decide if validation is to be done here. Do so if yes.
03ac148f
SK
132 List.
133
99fd18ae
SK
134-spec validate_unique_presence(T, [K]) ->
135 hope_result:t(T, [presence_error(K)])
136 when T :: t(K, _V).
fa24061d
SK
137validate_unique_presence(T, KeysRequired) ->
138 KeysOptional = [],
139 validate_unique_presence(T, KeysRequired, KeysOptional).
140
141-spec validate_unique_presence(t(K, _V), [K], [K]) ->
99fd18ae
SK
142 hope_result:t(T, [presence_error(K)])
143 when T :: t(K, _V).
fa24061d 144validate_unique_presence(T, KeysRequired, KeysOptional) ->
e163be89
SK
145 case find_unique_presence_violations(T, KeysRequired, KeysOptional)
146 of #hope_kv_list_presence_violations
147 { keys_missing = []
148 , keys_duplicated = []
149 , keys_unsupported = []
150 } ->
99fd18ae 151 {ok, T}
dfcd229c
SK
152 ; #hope_kv_list_presence_violations{}=Violations ->
153 {error, presence_violations_to_list(Violations)}
fa24061d
SK
154 end.
155
e163be89
SK
156-spec find_unique_presence_violations(t(K, _V), [K]) ->
157 presence_violations(K).
158find_unique_presence_violations(T, KeysRequired) ->
159 KeysOptional = [],
160 find_unique_presence_violations(T, KeysRequired, KeysOptional).
161
162-spec find_unique_presence_violations(t(K, _V), [K], [K]) ->
163 presence_violations(K).
164find_unique_presence_violations(T, KeysRequired, KeysOptional) ->
165 KeysSupported = KeysRequired ++ KeysOptional,
166 KeysGiven = [K || {K, _V} <- T],
167 KeysGivenUnique = lists:usort(KeysGiven),
168 KeysDuplicated = lists:usort(KeysGiven -- KeysGivenUnique),
169 KeysMissing = KeysRequired -- KeysGivenUnique,
170 KeysUnsupported = KeysGivenUnique -- KeysSupported,
171 #hope_kv_list_presence_violations
172 { keys_missing = KeysMissing
173 , keys_duplicated = KeysDuplicated
174 , keys_unsupported = KeysUnsupported
175 }.
176
dfcd229c
SK
177-spec presence_violations_to_list(presence_violations(K)) ->
178 [presence_error(K)].
179presence_violations_to_list(#hope_kv_list_presence_violations
180{ keys_missing = KeysMissing
181, keys_duplicated = KeysDuplicated
182, keys_unsupported = KeysUnsupported
183}) ->
184 ErrorMissing =
185 case KeysMissing
186 of [] -> []
187 ; [_|_] -> [{keys_missing, KeysMissing}]
188 end,
189 ErrorDups =
190 case KeysDuplicated
191 of [] -> []
192 ; [_|_] -> [{keys_duplicated, KeysDuplicated}]
193 end,
194 ErrorUnsupported =
195 case KeysUnsupported
196 of [] -> []
197 ; [_|_] -> [{keys_unsupported, KeysUnsupported}]
198 end,
199 ErrorDups ++ ErrorMissing ++ ErrorUnsupported.
200
03ac148f
SK
201
202%% ============================================================================
203%% Helpers
204%% ============================================================================
205
48371999
SK
206-spec lift_map(F) ->
207 G
208 when F :: fun(( K, V1 ) -> V2)
209 , G :: fun(({K, V1}) -> V2)
210 .
d10156b0
SK
211lift_map(F) ->
212 fun (X) -> apply_map(F, X) end.
213
48371999
SK
214-spec apply_map(fun((K, V1) -> V2), {K, V1}) ->
215 V2.
d10156b0
SK
216apply_map(F, {K, V}) ->
217 F(K, V).
This page took 0.059884 seconds and 4 git commands to generate.