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, to_binary_kv/2]). |
27 |
|
|
28 |
|
-include("mod_muc_light.hrl"). |
29 |
|
|
30 |
|
-export_type([binary_kv/0, kv/0, schema/0]). |
31 |
|
|
32 |
|
%% Config primitives |
33 |
|
-type key() :: atom(). |
34 |
|
-type value() :: binary() | integer() | float(). |
35 |
|
-type value_type() :: binary | integer | float. |
36 |
|
|
37 |
|
%% Actual config |
38 |
|
-type item() :: {key(), value()}. |
39 |
|
-type kv() :: [item()]. |
40 |
|
-type binary_kv() :: [{Key :: binary(), Value :: binary()}]. |
41 |
|
|
42 |
|
%% User definition processing |
43 |
|
-type schema_item() :: {FieldName :: binary(), DefaultValue :: value(), |
44 |
|
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 |
29 |
from_binary_kv_diff(lists:ukeysort(1, RawConfig), ConfigSchema, []). |
56 |
|
|
57 |
|
from_binary_kv_diff([], [], Config) -> |
58 |
:-( |
{ok, Config}; |
59 |
|
from_binary_kv_diff(RawConfig, ConfigSchema, Config) -> |
60 |
29 |
case take_next_kv(RawConfig, ConfigSchema) of |
61 |
|
{error, Reason} -> |
62 |
:-( |
{error, Reason}; |
63 |
|
{value, RRawConfig, RConfigSchema, KV} -> |
64 |
15 |
from_binary_kv(RRawConfig, RConfigSchema, [KV | Config]); |
65 |
|
{default, _, _, _} -> |
66 |
|
% do not populate the diff with default values |
67 |
14 |
from_binary_kv(RawConfig, ConfigSchema, Config) |
68 |
|
end. |
69 |
|
|
70 |
|
-spec from_binary_kv(RawConfig :: binary_kv(), ConfigSchema :: schema()) -> |
71 |
|
{ok, kv()} | validation_error(). |
72 |
|
from_binary_kv(RawConfig, ConfigSchema) -> |
73 |
428 |
from_binary_kv(lists:ukeysort(1, RawConfig), ConfigSchema, []). |
74 |
|
|
75 |
|
from_binary_kv([], [], Config) -> |
76 |
454 |
{ok, Config}; |
77 |
|
from_binary_kv(RawConfig, ConfigSchema, Config) -> |
78 |
1129 |
case take_next_kv(RawConfig, ConfigSchema) of |
79 |
|
{error, Reason} -> |
80 |
3 |
{error, Reason}; |
81 |
|
{_, RRawConfig, RConfigSchema, KV} -> |
82 |
1126 |
from_binary_kv(RRawConfig, RConfigSchema, [KV | Config]) |
83 |
|
end. |
84 |
|
|
85 |
|
take_next_kv([{KeyBin, ValBin} | RRawConfig], [{KeyBin, _Default, Key, Type} | RSchema]) -> |
86 |
516 |
try {value, RRawConfig, RSchema, {Key, b2value(ValBin, Type)}} |
87 |
1 |
catch _:_ -> {error, {KeyBin, type_error}} |
88 |
|
end; |
89 |
|
take_next_kv(RawConfig, [{_KeyBin, Default, Key, _Type} | RSchema]) -> |
90 |
640 |
{default, RawConfig, RSchema, {Key, Default}}; |
91 |
|
take_next_kv([{KeyBin, _} | _], _) -> |
92 |
2 |
{error, {KeyBin, not_found}}. |
93 |
|
|
94 |
|
-spec to_binary_kv(Config :: kv(), ConfigSchema :: schema()) -> binary_kv(). |
95 |
|
to_binary_kv(Config, ConfigSchema) -> |
96 |
254 |
ConfigWithSchema = lists:zip(lists:sort(Config), lists:keysort(3, ConfigSchema)), |
97 |
254 |
[{KeyBin, value2b(Val, Type)} || {{Key, Val}, {KeyBin, _Default, Key, Type}} <- ConfigWithSchema]. |
98 |
|
|
99 |
|
%%==================================================================== |
100 |
|
%% Internal functions |
101 |
|
%%==================================================================== |
102 |
|
|
103 |
|
-spec b2value(ValBin :: binary(), Type :: value_type()) -> Converted :: value(). |
104 |
515 |
b2value(ValBin, binary) when is_binary(ValBin) -> ValBin; |
105 |
:-( |
b2value(ValBin, integer) -> binary_to_integer(ValBin); |
106 |
:-( |
b2value(ValBin, float) -> binary_to_float(ValBin). |
107 |
|
|
108 |
|
-spec value2b(Val :: value(), Type :: value_type()) -> Converted :: binary(). |
109 |
508 |
value2b(Val, binary) when is_binary(Val) -> Val; |
110 |
:-( |
value2b(Val, integer) -> integer_to_binary(Val); |
111 |
:-( |
value2b(Val, float) -> float_to_binary(Val). |