1 |
|
%%%---------------------------------------------------------------------- |
2 |
|
%%% File : mod_muc_light_room_config.erl |
3 |
|
%%% Author : Piotr Nosek <piotr.nosek@erlang-solutions.com> |
4 |
|
%%% Purpose : Stateless utilities for room config processing |
5 |
|
%%% Created : 15 Nov 2019 by Piotr Nosek <piotr.nosek@erlang-solutions.com> |
6 |
|
%%% |
7 |
|
%%% This program is free software; you can redistribute it and/or |
8 |
|
%%% modify it under the terms of the GNU General Public License as |
9 |
|
%%% published by the Free Software Foundation; either version 2 of the |
10 |
|
%%% License, or (at your option) any later version. |
11 |
|
%%% |
12 |
|
%%% This program is distributed in the hope that it will be useful, |
13 |
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 |
|
%%% General Public License for more details. |
16 |
|
%%% |
17 |
|
%%% You should have received a copy of the GNU General Public License |
18 |
|
%%% along with this program; if not, write to the Free Software |
19 |
|
%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 |
|
%%% |
21 |
|
%%%---------------------------------------------------------------------- |
22 |
|
|
23 |
|
-module(mod_muc_light_room_config). |
24 |
|
|
25 |
|
%% API |
26 |
|
-export([from_binary_kv_diff/2, from_binary_kv/2, |
27 |
|
to_binary_kv_diff/2, to_binary_kv/2]). |
28 |
|
|
29 |
|
-include("mod_muc_light.hrl"). |
30 |
|
|
31 |
|
-export_type([binary_kv/0, kv/0, schema/0]). |
32 |
|
|
33 |
|
%% Config primitives |
34 |
|
-type key() :: atom(). |
35 |
|
-type value() :: binary() | integer() | float(). |
36 |
|
-type value_type() :: binary | integer | float. |
37 |
|
|
38 |
|
%% Actual config |
39 |
|
-type item() :: {key(), value()}. |
40 |
|
-type kv() :: [item()]. |
41 |
|
-type binary_kv() :: [{Key :: binary(), Value :: binary()}]. |
42 |
|
|
43 |
|
%% User definition processing |
44 |
|
-type schema_item() :: {FieldName :: binary(), DefaultValue :: value(), key(), value_type()}. |
45 |
|
-type schema() :: [schema_item()]. % has to be sorted |
46 |
|
|
47 |
|
%%==================================================================== |
48 |
|
%% API |
49 |
|
%%==================================================================== |
50 |
|
|
51 |
|
%% Guarantees that config will have unique fields |
52 |
|
-spec from_binary_kv_diff(RawConfig :: binary_kv(), ConfigSchema :: schema()) -> |
53 |
|
{ok, kv()} | validation_error(). |
54 |
|
from_binary_kv_diff(RawConfig, ConfigSchema) -> |
55 |
31 |
take_next(lists:ukeysort(1, RawConfig), ConfigSchema, true, fun take_next_kv/2, []). |
56 |
|
|
57 |
|
-spec from_binary_kv(RawConfig :: binary_kv(), ConfigSchema :: schema()) -> |
58 |
|
{ok, kv()} | validation_error(). |
59 |
|
from_binary_kv(RawConfig, ConfigSchema) -> |
60 |
520 |
take_next(lists:ukeysort(1, RawConfig), ConfigSchema, false, fun take_next_kv/2, []). |
61 |
|
|
62 |
|
-spec to_binary_kv_diff(RawConfig :: kv(), ConfigSchema :: schema()) -> |
63 |
|
{ok, binary_kv()} | validation_error(). |
64 |
|
to_binary_kv_diff(RawConfig, ConfigSchema) -> |
65 |
29 |
take_next(lists:ukeysort(1, RawConfig), ConfigSchema, true, fun take_next_binary_kv/2, []). |
66 |
|
|
67 |
|
-spec to_binary_kv(Config :: kv(), ConfigSchema :: schema()) -> |
68 |
|
{ok, binary_kv()} | validation_error(). |
69 |
|
to_binary_kv(RawConfig, ConfigSchema) -> |
70 |
445 |
take_next(lists:ukeysort(1, RawConfig), ConfigSchema, false, fun take_next_binary_kv/2, []). |
71 |
|
|
72 |
|
take_next([], [], _, _, Config) -> |
73 |
1022 |
{ok, Config}; |
74 |
|
take_next(RawConfig, ConfigSchema, DropDefaults, TakeNext, Config) -> |
75 |
2540 |
case {DropDefaults, TakeNext(RawConfig, ConfigSchema)} of |
76 |
|
{true, {default, RRawConfig, RConfigSchema, _}} -> |
77 |
|
% do not populate the diff with default values |
78 |
56 |
take_next(RRawConfig, RConfigSchema, DropDefaults, TakeNext, Config); |
79 |
|
{_, {_, RRawConfig, RConfigSchema, KV}} -> |
80 |
2481 |
take_next(RRawConfig, RConfigSchema, DropDefaults, TakeNext, [KV | Config]); |
81 |
|
{_, {error, Reason}} -> |
82 |
3 |
{error, Reason} |
83 |
|
end. |
84 |
|
|
85 |
|
%%==================================================================== |
86 |
|
%% Internal functions |
87 |
|
%%==================================================================== |
88 |
|
|
89 |
|
take_next_kv([{KeyBin, ValBin} | RRawConfig], [{KeyBin, _Default, Key, Type} | RSchema]) -> |
90 |
605 |
try {value, RRawConfig, RSchema, {Key, b2value(ValBin, Type)}} |
91 |
1 |
catch _:_ -> {error, {KeyBin, type_error}} |
92 |
|
end; |
93 |
|
take_next_kv(RawConfig, [{_KeyBin, Default, Key, _Type} | RSchema]) -> |
94 |
767 |
{default, RawConfig, RSchema, {Key, Default}}; |
95 |
|
take_next_kv([{KeyBin, _} | _], _) -> |
96 |
2 |
{error, {KeyBin, not_found}}. |
97 |
|
|
98 |
|
take_next_binary_kv([{Key, ValBin} | RRawConfig], [{KeyBin, _Default, Key, Type} | RSchema]) -> |
99 |
1139 |
try {value, RRawConfig, RSchema, {KeyBin, value2b(ValBin, Type)}} |
100 |
:-( |
catch _:_ -> {error, {KeyBin, type_error}} |
101 |
|
end; |
102 |
|
take_next_binary_kv(RawConfig, [{KeyBin, Default, _Key, _Type} | RSchema]) -> |
103 |
27 |
{default, RawConfig, RSchema, {KeyBin, Default}}; |
104 |
|
take_next_binary_kv([{KeyBin, _} | _], _) -> |
105 |
:-( |
{error, {KeyBin, not_found}}. |
106 |
|
|
107 |
|
-spec b2value(ValBin :: binary(), Type :: value_type()) -> Converted :: value(). |
108 |
604 |
b2value(ValBin, binary) when is_binary(ValBin) -> ValBin; |
109 |
:-( |
b2value(ValBin, integer) -> binary_to_integer(ValBin); |
110 |
:-( |
b2value(ValBin, float) -> binary_to_float(ValBin). |
111 |
|
|
112 |
|
-spec value2b(Val :: value(), Type :: value_type()) -> Converted :: binary(). |
113 |
1139 |
value2b(Val, binary) when is_binary(Val) -> Val; |
114 |
:-( |
value2b(Val, integer) -> integer_to_binary(Val); |
115 |
:-( |
value2b(Val, float) -> float_to_binary(Val). |