Current section

Files

Jump to
crossbar src crossbar.erl
Raw

src/crossbar.erl

-module(crossbar).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([int/2, float/2, string/2, bool/2, string_value/1, int_value/1, float_value/1, bool_value/1, field_name/1, rule_to_string/1, rule_to_error_string/1, to_float/1, required/1, min_value/2, max_value/2, min_length/2, max_length/2, eq/3, not_eq/3, with_validator/4, regex/4, extract_errors/1, to_serializable/3, to_serializable_list/2, serializables_to_string/1, has_errors/1, validate/1, validate_many/2]).
-export_type([json_mode/0, cross_bar_error/0]).
-type json_mode() :: array | key_value.
-type cross_bar_error() :: {failed_rule, binary(), binary(), binary()}.
-spec int(binary(), integer()) -> crossbar@internal@field:field(integer()).
int(Name, Value) ->
{int_field, Name, Value, []}.
-spec float(binary(), float()) -> crossbar@internal@field:field(float()).
float(Name, Value) ->
{float_field, Name, Value, []}.
-spec string(binary(), binary()) -> crossbar@internal@field:field(binary()).
string(Name, Value) ->
{string_field, Name, Value, []}.
-spec bool(binary(), boolean()) -> crossbar@internal@field:field(boolean()).
bool(Name, Value) ->
{bool_field, Name, Value, []}.
-spec string_value(crossbar@internal@field:field(binary())) -> binary().
string_value(Field) ->
case Field of
{string_field, _, Value, _} ->
Value;
_ ->
<<""/utf8>>
end.
-spec int_value(crossbar@internal@field:field(integer())) -> integer().
int_value(Field) ->
case Field of
{int_field, _, Value, _} ->
Value;
_ ->
0
end.
-spec float_value(crossbar@internal@field:field(float())) -> float().
float_value(Field) ->
case Field of
{float_field, _, Value, _} ->
Value;
_ ->
+0.0
end.
-spec bool_value(crossbar@internal@field:field(boolean())) -> boolean().
bool_value(Field) ->
case Field of
{bool_field, _, Value, _} ->
Value;
_ ->
false
end.
-spec field_name(crossbar@internal@field:field(any())) -> binary().
field_name(Field) ->
case Field of
{int_field, Name, _, _} ->
Name;
{float_field, Name@1, _, _} ->
Name@1;
{string_field, Name@2, _, _} ->
Name@2;
{bool_field, Name@3, _, _} ->
Name@3
end.
-spec rule_to_string(crossbar@internal@field:rule(any())) -> binary().
rule_to_string(Rule) ->
case Rule of
required ->
<<"required"/utf8>>;
{min_size, _} ->
<<"min_value"/utf8>>;
{max_size, _} ->
<<"max_value"/utf8>>;
{min_length, _} ->
<<"min_length"/utf8>>;
{max_length, _} ->
<<"max_length"/utf8>>;
{eq, _, _} ->
<<"eq"/utf8>>;
{not_eq, _, _} ->
<<"not_eq"/utf8>>;
{regex, Name, _, _} ->
gleam@bool:guard(
Name =:= <<""/utf8>>,
<<"regex"/utf8>>,
fun() -> Name end
);
{validator_function, Name@1, _, _} ->
gleam@bool:guard(
Name@1 =:= <<""/utf8>>,
<<"validator_function"/utf8>>,
fun() -> Name@1 end
)
end.
-spec extract_last_error_part(binary(), any()) -> binary().
extract_last_error_part(Name, Value) ->
Name@1 = gleam@string:trim(Name),
gleam@bool:guard(
Name@1 /= <<""/utf8>>,
<<<<"`"/utf8, Name@1/binary>>/binary, "` field"/utf8>>,
fun() -> crossbar@internal@cast:to_string(Value) end
).
-spec rule_to_error_string(crossbar@internal@field:rule(any())) -> binary().
rule_to_error_string(Rule) ->
case Rule of
required ->
<<"is required"/utf8>>;
{min_size, V} ->
<<"must be at least "/utf8, (gleam@float:to_string(V))/binary>>;
{max_size, V@1} ->
<<"must not be greater than "/utf8,
(gleam@float:to_string(V@1))/binary>>;
{min_length, V@2} ->
<<<<"must be at least "/utf8, (gleam@int:to_string(V@2))/binary>>/binary,
" characters"/utf8>>;
{max_length, V@3} ->
<<<<"must not be longer than "/utf8,
(gleam@int:to_string(V@3))/binary>>/binary,
" characters"/utf8>>;
{eq, Name, Value} ->
<<"must be equal to "/utf8,
(extract_last_error_part(Name, Value))/binary>>;
{not_eq, Name@1, Value@1} ->
<<"must not be equal to "/utf8,
(extract_last_error_part(Name@1, Value@1))/binary>>;
{regex, _, _, Error} ->
Error;
{validator_function, _, _, Error@1} ->
Error@1
end.
-spec append_rule(
crossbar@internal@field:field(FXT),
crossbar@internal@field:rule(FXT)
) -> crossbar@internal@field:field(FXT).
append_rule(Field, Rule) ->
case Field of
{int_field, Name, Value, Rules} ->
{int_field, Name, Value, gleam@list:append(Rules, [Rule])};
{float_field, Name@1, Value@1, Rules@1} ->
{float_field, Name@1, Value@1, gleam@list:append(Rules@1, [Rule])};
{string_field, Name@2, Value@2, Rules@2} ->
{string_field, Name@2, Value@2, gleam@list:append(Rules@2, [Rule])};
{bool_field, Name@3, Value@3, Rules@3} ->
{bool_field, Name@3, Value@3, gleam@list:append(Rules@3, [Rule])}
end.
-spec int_rules_to_float_rules(list(crossbar@internal@field:rule(integer()))) -> list(crossbar@internal@field:rule(float())).
int_rules_to_float_rules(Rules) ->
_pipe = Rules,
gleam@list:map(_pipe, fun(Rule) -> case Rule of
required ->
required;
{min_size, V} ->
{min_size, V};
{max_size, V@1} ->
{max_size, V@1};
{min_length, V@2} ->
{min_length, V@2};
{max_length, V@3} ->
{max_length, V@3};
{eq, Name, Value} ->
{eq, Name, gleam@int:to_float(Value)};
{not_eq, Name@1, Value@1} ->
{not_eq, Name@1, gleam@int:to_float(Value@1)};
{regex, Name@2, Regex, Error} ->
{regex, Name@2, Regex, Error};
{validator_function, Name@3, Original_validator, Error@1} ->
{validator_function, Name@3, fun(V@4) -> _pipe@1 = V@4,
_pipe@2 = gleam@float:round(_pipe@1),
Original_validator(_pipe@2) end, Error@1}
end end).
-spec to_float(crossbar@internal@field:field(integer())) -> crossbar@internal@field:field(float()).
to_float(Field) ->
{int_field, Name, Value, Rules} = case Field of
{int_field, _, _, _} -> Field;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"crossbar"/utf8>>,
function => <<"to_float"/utf8>>,
line => 198})
end,
_pipe = Value,
_pipe@1 = gleam@int:to_float(_pipe),
{float_field, Name, _pipe@1, int_rules_to_float_rules(Rules)}.
-spec required(crossbar@internal@field:field(GDV)) -> crossbar@internal@field:field(GDV).
required(Field) ->
append_rule(Field, required).
-spec min_value(crossbar@internal@field:field(float()), float()) -> crossbar@internal@field:field(float()).
min_value(Field, Size) ->
append_rule(Field, {min_size, Size}).
-spec max_value(crossbar@internal@field:field(float()), float()) -> crossbar@internal@field:field(float()).
max_value(Field, Size) ->
append_rule(Field, {max_size, Size}).
-spec min_length(crossbar@internal@field:field(binary()), integer()) -> crossbar@internal@field:field(binary()).
min_length(Field, Length) ->
append_rule(Field, {min_length, Length}).
-spec max_length(crossbar@internal@field:field(binary()), integer()) -> crossbar@internal@field:field(binary()).
max_length(Field, Length) ->
append_rule(Field, {max_length, Length}).
-spec eq(crossbar@internal@field:field(FYT), binary(), FYT) -> crossbar@internal@field:field(FYT).
eq(Field, Name, Value) ->
append_rule(Field, {eq, Name, Value}).
-spec not_eq(crossbar@internal@field:field(FYW), binary(), FYW) -> crossbar@internal@field:field(FYW).
not_eq(Field, Name, Value) ->
append_rule(Field, {not_eq, Name, Value}).
-spec with_validator(
crossbar@internal@field:field(FYZ),
binary(),
fun((FYZ) -> boolean()),
binary()
) -> crossbar@internal@field:field(FYZ).
with_validator(Field, Name, Func, Error) ->
append_rule(Field, {validator_function, Name, Func, Error}).
-spec regex(
crossbar@internal@field:field(FZC),
binary(),
gleam@regex:regex(),
binary()
) -> crossbar@internal@field:field(FZC).
regex(Field, Name, Regex, Error) ->
append_rule(Field, {regex, Name, Regex, Error}).
-spec extract_errors(
{ok, crossbar@internal@field:field(any())} |
{error, list(cross_bar_error())}
) -> list({binary(), binary()}).
extract_errors(Result) ->
Errors@1 = case Result of
{ok, _} ->
[];
{error, Errors} ->
Errors
end,
gleam@list:map(Errors@1, fun(Error) -> case Error of
{failed_rule, _, Rule, Error@1} ->
{Rule, Error@1}
end end).
-spec extract_field_name(
{ok, crossbar@internal@field:field(any())} |
{error, list(cross_bar_error())}
) -> binary().
extract_field_name(Result) ->
case Result of
{ok, Field} ->
erlang:element(2, Field);
{error, Errors} ->
case Errors of
[{failed_rule, Name, _, _} | _] ->
Name;
[] ->
<<""/utf8>>
end
end.
-spec to_serializable(
{ok, crossbar@internal@field:field(any())} |
{error, list(cross_bar_error())},
binary(),
json_mode()
) -> {binary(), gleam@json:json()}.
to_serializable(Validation_result, Field_name, Mode) ->
Name = case Field_name of
<<""/utf8>> ->
extract_field_name(Validation_result);
_ ->
Field_name
end,
Errors = begin
_pipe = Validation_result,
extract_errors(_pipe)
end,
gleam@bool:guard(
Errors =:= [],
{Name, gleam@json:null()},
fun() ->
Json_value = case Mode of
array ->
Errors_list = gleam@list:map(
Errors,
fun(E) -> erlang:element(2, E) end
),
gleam@json:array(Errors_list, fun gleam@json:string/1);
key_value ->
gleam@json:object(
gleam@list:map(
Errors,
fun(E@1) ->
{erlang:element(1, E@1),
gleam@json:string(erlang:element(2, E@1))}
end
)
)
end,
{Name, Json_value}
end
).
-spec to_serializable_list(
list({binary(), list(cross_bar_error())}),
json_mode()
) -> list({binary(), gleam@json:json()}).
to_serializable_list(Results, Mode) ->
_pipe = Results,
gleam@list:map(
_pipe,
fun(R) ->
to_serializable(
{error, erlang:element(2, R)},
erlang:element(1, R),
Mode
)
end
).
-spec serializables_to_string(list({binary(), gleam@json:json()})) -> binary().
serializables_to_string(Serializables) ->
_pipe = Serializables,
_pipe@1 = gleam@json:object(_pipe),
gleam@json:to_string(_pipe@1).
-spec has_errors(list({binary(), gleam@json:json()})) -> boolean().
has_errors(Json_errors) ->
_pipe = Json_errors,
_pipe@1 = gleam@list:map(_pipe, fun(E) -> erlang:element(2, E) end),
gleam@list:any(_pipe@1, fun(E@1) -> E@1 /= gleam@json:null() end).
-spec validate_field(
crossbar@internal@field:field(FZR),
list(crossbar@internal@field:rule(FZR)),
list(cross_bar_error())
) -> list(cross_bar_error()).
validate_field(Field, Rules, Errors) ->
case Rules of
[Rule | Other_rules] ->
Validation_result = case Rule of
required ->
crossbar@internal@field:validate_required(Field);
{min_size, Size} ->
crossbar@internal@field:validate_min_value(Field, Size);
{max_size, Size@1} ->
crossbar@internal@field:validate_max_value(Field, Size@1);
{min_length, Length} ->
crossbar@internal@field:validate_min_length(Field, Length);
{max_length, Length@1} ->
crossbar@internal@field:validate_max_length(Field, Length@1);
{eq, _, Value} ->
crossbar@internal@field:validate_eq(Field, Value);
{not_eq, _, Value@1} ->
crossbar@internal@field:validate_not_eq(Field, Value@1);
{regex, _, Regex, _} ->
crossbar@internal@field:validate_regex(Field, Regex);
{validator_function, _, Func, _} ->
crossbar@internal@field:use_validator_function(Field, Func)
end,
New_errors = (gleam@bool:guard(
Validation_result,
Errors,
fun() -> _pipe = Errors,
gleam@list:append(
_pipe,
[{failed_rule,
erlang:element(2, Field),
rule_to_string(Rule),
rule_to_error_string(Rule)}]
) end
)),
validate_field(Field, Other_rules, New_errors);
[] ->
Errors
end.
-spec validate(crossbar@internal@field:field(FZX)) -> {ok,
crossbar@internal@field:field(FZX)} |
{error, list(cross_bar_error())}.
validate(Field) ->
Validation_result = validate_field(Field, erlang:element(4, Field), []),
case Validation_result of
[] ->
{ok, Field};
_ ->
{error, Validation_result}
end.
-spec validate_many(list(crossbar@internal@field:field(any())), boolean()) -> list({binary(),
list(cross_bar_error())}).
validate_many(Fields, Failed_only) ->
_pipe = Fields,
_pipe@1 = gleam@list:map(
_pipe,
fun(Field) ->
Errors@1 = case validate(Field) of
{ok, _} ->
[];
{error, Errors} ->
Errors
end,
{erlang:element(2, Field), Errors@1}
end
),
(fun(Results) -> case Failed_only of
true ->
gleam@list:filter(
Results,
fun(Res) ->
gleam@list:length(erlang:element(2, Res)) > 0
end
);
false ->
Results
end end)(_pipe@1).