Implement & test hope_kv_list:validate_unique_presence/(2|3)
[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
6-behavior(hope_dictionary).
7
8-export_type(
9 [ t/2
10 ]).
11
12-export(
13 [ empty/0
14 , get/2
15 , set/3
16 , update/3
70cf8e86 17 , pop/2
03ac148f
SK
18 , iter/2
19 , map/2
20 , filter/2
21 , fold/3
22 , of_kv_list/1
23 , to_kv_list/1
fa24061d
SK
24 , validate_unique_presence/2 % Assume default optional parameter(s)
25 , validate_unique_presence/3 % Specify optional parameter(s)
03ac148f
SK
26 ]).
27
28
29-type t(K, V) ::
30 [{K, V}].
31
fa24061d
SK
32-type presence_error(A) ::
33 {keys_missing , [A]}
34 | {keys_duplicated , [A]}
35 | {keys_unsupported , [A]}
36 .
37
03ac148f
SK
38
39%% ============================================================================
40%% API
41%% ============================================================================
42
43-spec empty() ->
44 [].
45empty() ->
46 [].
47
95c3c2a7
PO
48-spec get(t(K, V), K) ->
49 hope_option:t(V).
03ac148f
SK
50get(T, K) ->
51 case lists:keyfind(K, 1, T)
52 of false -> none
53 ; {K, V} -> {some, V}
54 end.
55
95c3c2a7
PO
56-spec set(t(K, V), K, V) ->
57 t(K, V).
03ac148f
SK
58set(T, K, V) ->
59 lists:keystore(K, 1, T, {K, V}).
60
95c3c2a7
PO
61-spec update(t(K, V), K, fun((hope_option:t(V)) -> V)) ->
62 t(K, V).
03ac148f
SK
63update(T, K, F) ->
64 V1Opt = get(T, K),
65 V2 = F(V1Opt),
37c6e98b 66 % TODO: Eliminate the 2nd lookup.
03ac148f
SK
67 set(T, K, V2).
68
95c3c2a7
PO
69-spec pop(t(K, V), K) ->
70 {hope_option:t(V), t(K, V)}.
70cf8e86
SK
71pop(T1, K) ->
72 case lists:keytake(K, 1, T1)
73 of {value, {K, V}, T2} -> {{some, V}, T2}
74 ; false -> {none , T1}
75 end.
76
95c3c2a7
PO
77-spec iter(t(K, V), fun((K, V) -> ok)) ->
78 ok.
c1672ac4 79iter(T, F1) ->
d10156b0 80 F2 = lift_map(F1),
c1672ac4 81 lists:foreach(F2, T).
03ac148f 82
95c3c2a7
PO
83-spec map(t(K, V), fun((K, V) -> V)) ->
84 t(K, V).
c1672ac4 85map(T, F1) ->
d10156b0
SK
86 F2 = fun ({K, _}=X) -> {K, apply_map(F1, X)} end,
87 lists:map(F2, T).
03ac148f 88
95c3c2a7
PO
89-spec filter(t(K, V), fun((K, V) -> boolean())) ->
90 t(K, V).
c1672ac4 91filter(T, F1) ->
d10156b0 92 F2 = lift_map(F1),
c1672ac4 93 lists:filter(F2, T).
03ac148f 94
95c3c2a7
PO
95-spec fold(t(K, V), fun((K, V, Acc) -> Acc), Acc) ->
96 Acc.
03ac148f
SK
97fold(T, F1, Accumulator) ->
98 F2 = fun ({K, V}, Acc) -> F1(K, V, Acc) end,
097a2aa3 99 lists:foldl(F2, Accumulator, T).
03ac148f 100
95c3c2a7
PO
101-spec to_kv_list(t(K, V)) ->
102 [{K, V}].
03ac148f
SK
103to_kv_list(T) ->
104 T.
105
95c3c2a7
PO
106-spec of_kv_list([{K, V}]) ->
107 t(K, V).
03ac148f 108of_kv_list(List) ->
37c6e98b 109 % TODO: Decide if validation is to be done here. Do so if yes.
03ac148f
SK
110 List.
111
fa24061d
SK
112-spec validate_unique_presence(t(K, _V), [K]) ->
113 hope_result:t(ok, [presence_error(K)]).
114validate_unique_presence(T, KeysRequired) ->
115 KeysOptional = [],
116 validate_unique_presence(T, KeysRequired, KeysOptional).
117
118-spec validate_unique_presence(t(K, _V), [K], [K]) ->
119 hope_result:t(ok, [presence_error(K)]).
120validate_unique_presence(T, KeysRequired, KeysOptional) ->
121 KeysSupported = KeysRequired ++ KeysOptional,
122 KeysGiven = [K || {K, _V} <- T],
123 KeysGivenUnique = lists:usort(KeysGiven),
124 KeysDups = lists:usort(KeysGiven -- KeysGivenUnique),
125 KeysMissing = KeysRequired -- KeysGivenUnique,
126 KeysUnsupported = KeysGivenUnique -- KeysSupported,
127 case {KeysDups, KeysMissing, KeysUnsupported}
128 of {[], [], []} ->
129 {ok, ok}
130 ; {Dups, Missing, Unsupported} ->
131 ErrorDups =
132 case Dups
133 of [] -> []
134 ; [_|_] -> [{keys_duplicated, Dups}]
135 end,
136 ErrorMissing =
137 case Missing
138 of [] -> []
139 ; [_|_] -> [{keys_missing, Missing}]
140 end,
141 ErrorUnsupported =
142 case Unsupported
143 of [] -> []
144 ; [_|_] -> [{keys_unsupported, Unsupported}]
145 end,
146 Errors = ErrorDups ++ ErrorMissing ++ ErrorUnsupported,
147 {error, Errors}
148 end.
149
03ac148f
SK
150
151%% ============================================================================
152%% Helpers
153%% ============================================================================
154
48371999
SK
155-spec lift_map(F) ->
156 G
157 when F :: fun(( K, V1 ) -> V2)
158 , G :: fun(({K, V1}) -> V2)
159 .
d10156b0
SK
160lift_map(F) ->
161 fun (X) -> apply_map(F, X) end.
162
48371999
SK
163-spec apply_map(fun((K, V1) -> V2), {K, V1}) ->
164 V2.
d10156b0
SK
165apply_map(F, {K, V}) ->
166 F(K, V).
This page took 0.035255 seconds and 4 git commands to generate.