Current section
Files
Jump to
Current section
Files
src/pedantic@decode.erl
-module(pedantic@decode).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/pedantic/decode.gleam").
-export([decode_int/1, decode_exact_int/1, decode_float/1, decode_exact_float/1, decode_bool/1, decode_exact_bool/1, decode_string/1, decode_exact_string/1, list/1, optional/1, dict/1, default/2, object/2, field/3, key_optional/3, key_with_default/4, build/1, with_error_formatter/2, with_error_message/2, with_template/2, refine/2, assert_that/3, check/3, min/2, max/2, min_length/2, max_length/2, positive/1, negative/1, non_negative/1, non_positive/1, map/2, trim/1, lowercase/1, uppercase/1, collapse_whitespace/1, replace/3, remove_chars/2, allow_chars/2, strip_control_chars/1, normalize_email/1, normalize_uuid/1, default_if_blank/2, empty_as_none/1, length/2, email/2, uuid/2, any_of/1, min_size/2, max_size/2, int_range/2, float_range/2, string_limit/2, decode_non_empty_string/1, non_empty_list/1, title/2, describe/2, example/2]).
-export_type([decoder/1, object_builder/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type decoder(DPH) :: {decoder,
pedantic@report:type_graph(),
fun((gleam@dynamic:dynamic_()) -> {ok, DPH} |
{error, pedantic@report:report()})}.
-opaque object_builder(DPI, DPJ) :: {object_builder,
binary(),
list(pedantic@report:type_field()),
fun((gleam@dynamic:dynamic_()) -> {ok, DPI} |
{error, pedantic@report:report()})} |
{gleam_phantom, DPJ}.
-file("src/pedantic/decode.gleam", 43).
-spec decode_int(gleam@dynamic:dynamic_()) -> {ok, integer()} |
{error, pedantic@report:report()}.
decode_int(Dyn) ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_int/1}
) of
{ok, I} ->
{ok, I};
{error, _} ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_string/1}
) of
{ok, S} ->
case gleam_stdlib:parse_int(S) of
{ok, I@1} ->
{ok, I@1};
{error, _} ->
Err = pedantic@report:make_error(
[],
{wrong_type, <<"Int"/utf8>>, <<"String"/utf8>>},
Dyn
),
{error, {report, [Err], []}}
end;
{error, _} ->
Err@1 = pedantic@report:make_error(
[],
{wrong_type,
<<"Int"/utf8>>,
gleam_stdlib:classify_dynamic(Dyn)},
Dyn
),
{error, {report, [Err@1], []}}
end
end.
-file("src/pedantic/decode.gleam", 78).
-spec decode_exact_int(gleam@dynamic:dynamic_()) -> {ok, integer()} |
{error, pedantic@report:report()}.
decode_exact_int(Dyn) ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_int/1}
) of
{ok, I} ->
{ok, I};
{error, _} ->
Err = pedantic@report:make_error(
[],
{wrong_type, <<"Int"/utf8>>, gleam_stdlib:classify_dynamic(Dyn)},
Dyn
),
{error, {report, [Err], []}}
end.
-file("src/pedantic/decode.gleam", 99).
-spec decode_float(gleam@dynamic:dynamic_()) -> {ok, float()} |
{error, pedantic@report:report()}.
decode_float(Dyn) ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_float/1}
) of
{ok, F} ->
{ok, F};
{error, _} ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_int/1}
) of
{ok, I} ->
{ok, erlang:float(I)};
{error, _} ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_string/1}
) of
{ok, S} ->
case gleam_stdlib:parse_float(S) of
{ok, F@1} ->
{ok, F@1};
{error, _} ->
Err = pedantic@report:make_error(
[],
{wrong_type,
<<"Float"/utf8>>,
<<"String"/utf8>>},
Dyn
),
{error, {report, [Err], []}}
end;
{error, _} ->
Err@1 = pedantic@report:make_error(
[],
{wrong_type,
<<"Float"/utf8>>,
gleam_stdlib:classify_dynamic(Dyn)},
Dyn
),
{error, {report, [Err@1], []}}
end
end
end.
-file("src/pedantic/decode.gleam", 143).
-spec decode_exact_float(gleam@dynamic:dynamic_()) -> {ok, float()} |
{error, pedantic@report:report()}.
decode_exact_float(Dyn) ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_float/1}
) of
{ok, F} ->
{ok, F};
{error, _} ->
Err = pedantic@report:make_error(
[],
{wrong_type,
<<"Float"/utf8>>,
gleam_stdlib:classify_dynamic(Dyn)},
Dyn
),
{error, {report, [Err], []}}
end.
-file("src/pedantic/decode.gleam", 164).
-spec decode_bool(gleam@dynamic:dynamic_()) -> {ok, boolean()} |
{error, pedantic@report:report()}.
decode_bool(Dyn) ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_bool/1}
) of
{ok, B} ->
{ok, B};
{error, _} ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_string/1}
) of
{ok, S} ->
case S of
<<"true"/utf8>> ->
{ok, true};
<<"1"/utf8>> ->
{ok, true};
<<"false"/utf8>> ->
{ok, false};
<<"0"/utf8>> ->
{ok, false};
_ ->
Err = pedantic@report:make_error(
[],
{wrong_type, <<"Bool"/utf8>>, <<"String"/utf8>>},
Dyn
),
{error, {report, [Err], []}}
end;
{error, _} ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_int/1}
) of
{ok, 1} ->
{ok, true};
{ok, 0} ->
{ok, false};
_ ->
Err@1 = pedantic@report:make_error(
[],
{wrong_type,
<<"Bool"/utf8>>,
gleam_stdlib:classify_dynamic(Dyn)},
Dyn
),
{error, {report, [Err@1], []}}
end
end
end.
-file("src/pedantic/decode.gleam", 206).
-spec decode_exact_bool(gleam@dynamic:dynamic_()) -> {ok, boolean()} |
{error, pedantic@report:report()}.
decode_exact_bool(Dyn) ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_bool/1}
) of
{ok, B} ->
{ok, B};
{error, _} ->
Err = pedantic@report:make_error(
[],
{wrong_type,
<<"Bool"/utf8>>,
gleam_stdlib:classify_dynamic(Dyn)},
Dyn
),
{error, {report, [Err], []}}
end.
-file("src/pedantic/decode.gleam", 227).
-spec decode_string(gleam@dynamic:dynamic_()) -> {ok, binary()} |
{error, pedantic@report:report()}.
decode_string(Dyn) ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_string/1}
) of
{ok, S} ->
{ok, S};
{error, _} ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_int/1}
) of
{ok, I} ->
{ok, erlang:integer_to_binary(I)};
{error, _} ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_float/1}
) of
{ok, F} ->
{ok, gleam_stdlib:float_to_string(F)};
{error, _} ->
case gleam@dynamic@decode:run(
Dyn,
{decoder,
fun gleam@dynamic@decode:decode_bool/1}
) of
{ok, true} ->
{ok, <<"true"/utf8>>};
{ok, false} ->
{ok, <<"false"/utf8>>};
{error, _} ->
Err = pedantic@report:make_error(
[],
{wrong_type,
<<"String"/utf8>>,
gleam_stdlib:classify_dynamic(Dyn)},
Dyn
),
{error, {report, [Err], []}}
end
end
end
end.
-file("src/pedantic/decode.gleam", 264).
-spec decode_exact_string(gleam@dynamic:dynamic_()) -> {ok, binary()} |
{error, pedantic@report:report()}.
decode_exact_string(Dyn) ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_string/1}
) of
{ok, S} ->
{ok, S};
{error, _} ->
Err = pedantic@report:make_error(
[],
{wrong_type,
<<"String"/utf8>>,
gleam_stdlib:classify_dynamic(Dyn)},
Dyn
),
{error, {report, [Err], []}}
end.
-file("src/pedantic/decode.gleam", 293).
?DOC(" List combinator that decodes dynamic arrays into typed Lists, accumulating errors.\n").
-spec list(decoder(DQA)) -> decoder(list(DQA)).
list(Element_decoder) ->
Function = fun(Dyn) ->
case gleam@dynamic@decode:run(
Dyn,
gleam@dynamic@decode:list(
{decoder, fun gleam@dynamic@decode:decode_dynamic/1}
)
) of
{ok, Dyn_list} ->
{Results, Reports} = begin
_pipe = gleam@list:index_map(
Dyn_list,
fun(Elem_dyn, Idx) ->
case (erlang:element(3, Element_decoder))(Elem_dyn) of
{ok, V} ->
{ok, V};
{error, Rep} ->
{error,
pedantic@report:prefix_path(
Rep,
{index, Idx}
)}
end
end
),
gleam@list:partition(_pipe, fun(Res) -> case Res of
{ok, _} ->
true;
{error, _} ->
false
end end)
end,
case gleam@list:is_empty(Reports) of
true ->
Values = gleam@list:map(
Results,
fun(R) ->
V@2 = case R of
{ok, V@1} -> V@1;
_assert_fail ->
erlang:error(
#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"pedantic/decode"/utf8>>,
function => <<"list"/utf8>>,
line => 315,
value => _assert_fail,
start => 8712,
'end' => 8732,
pattern_start => 8723,
pattern_end => 8728}
)
end,
V@2
end
),
{ok, Values};
false ->
Combined = gleam@list:fold(
Reports,
pedantic@report:new(),
fun(Acc, Next_rep) ->
Rep@2 = case Next_rep of
{error, Rep@1} -> Rep@1;
_assert_fail@1 ->
erlang:error(
#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"pedantic/decode"/utf8>>,
function => <<"list"/utf8>>,
line => 323,
value => _assert_fail@1,
start => 8934,
'end' => 8966,
pattern_start => 8945,
pattern_end => 8955}
)
end,
pedantic@report:merge(Acc, Rep@2)
end
),
{error, Combined}
end;
{error, _} ->
Err = pedantic@report:make_error(
[],
{wrong_type,
<<"List"/utf8>>,
gleam_stdlib:classify_dynamic(Dyn)},
Dyn
),
{error, {report, [Err], []}}
end
end,
{decoder, {list, erlang:element(2, Element_decoder)}, Function}.
-file("src/pedantic/decode.gleam", 346).
?DOC(" Option/Optional combinator representing absent or nullable values.\n").
-spec optional(decoder(DQE)) -> decoder(gleam@option:option(DQE)).
optional(Inner_decoder) ->
Function = fun(Dyn) -> case pedantic_ffi:is_null_or_undefined(Dyn) of
true ->
{ok, none};
false ->
case gleam@dynamic@decode:run(
Dyn,
{decoder, fun gleam@dynamic@decode:decode_string/1}
) of
{ok, <<"null"/utf8>>} ->
{ok, none};
{ok, <<"undefined"/utf8>>} ->
{ok, none};
_ ->
case (erlang:element(3, Inner_decoder))(Dyn) of
{ok, V} ->
{ok, {some, V}};
{error, Rep} ->
{error, Rep}
end
end
end end,
{decoder, {option, erlang:element(2, Inner_decoder)}, Function}.
-file("src/pedantic/decode.gleam", 368).
?DOC(" Dict combinator to decode dynamic objects with string keys, accumulating errors.\n").
-spec dict(decoder(DQI)) -> decoder(gleam@dict:dict(binary(), DQI)).
dict(Value_decoder) ->
Function = fun(Dyn) ->
case gleam@dynamic@decode:run(
Dyn,
gleam@dynamic@decode:dict(
{decoder, fun gleam@dynamic@decode:decode_string/1},
{decoder, fun gleam@dynamic@decode:decode_dynamic/1}
)
) of
{ok, Raw_dict} ->
{Results, Reports} = begin
_pipe = maps:to_list(Raw_dict),
_pipe@1 = gleam@list:map(
_pipe,
fun(Pair) ->
{K, V_dyn} = Pair,
case (erlang:element(3, Value_decoder))(V_dyn) of
{ok, V} ->
{ok, {K, V}};
{error, Rep} ->
{error,
pedantic@report:prefix_path(
Rep,
{field, K}
)}
end
end
),
gleam@list:partition(_pipe@1, fun(Res) -> case Res of
{ok, _} ->
true;
{error, _} ->
false
end end)
end,
case gleam@list:is_empty(Reports) of
true ->
Entries = gleam@list:map(
Results,
fun(R) ->
V@2 = case R of
{ok, V@1} -> V@1;
_assert_fail ->
erlang:error(
#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"pedantic/decode"/utf8>>,
function => <<"dict"/utf8>>,
line => 392,
value => _assert_fail,
start => 11000,
'end' => 11020,
pattern_start => 11011,
pattern_end => 11016}
)
end,
V@2
end
),
{ok, maps:from_list(Entries)};
false ->
Combined = gleam@list:fold(
Reports,
pedantic@report:new(),
fun(Acc, Next_rep) ->
Rep@2 = case Next_rep of
{error, Rep@1} -> Rep@1;
_assert_fail@1 ->
erlang:error(
#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"pedantic/decode"/utf8>>,
function => <<"dict"/utf8>>,
line => 400,
value => _assert_fail@1,
start => 11239,
'end' => 11271,
pattern_start => 11250,
pattern_end => 11260}
)
end,
pedantic@report:merge(Acc, Rep@2)
end
),
{error, Combined}
end;
{error, _} ->
Err = pedantic@report:make_error(
[],
{wrong_type,
<<"Dict"/utf8>>,
gleam_stdlib:classify_dynamic(Dyn)},
Dyn
),
{error, {report, [Err], []}}
end
end,
{decoder, {dict, erlang:element(2, Value_decoder)}, Function}.
-file("src/pedantic/decode.gleam", 423).
?DOC(" Adapts an optional decoder with a default value fallback.\n").
-spec default(decoder(gleam@option:option(DQN)), DQN) -> decoder(DQN).
default(Decoder, Default_val) ->
Function = fun(Dyn) -> case (erlang:element(3, Decoder))(Dyn) of
{ok, {some, V}} ->
{ok, V};
{ok, none} ->
{ok, Default_val};
{error, R} ->
{error, R}
end end,
{decoder, erlang:element(2, Decoder), Function}.
-file("src/pedantic/decode.gleam", 444).
?DOC(" Initializes an ObjectBuilder for a custom record constructor.\n").
-spec object(binary(), DQR) -> object_builder(DQR, any()).
object(Name, Constructor) ->
{object_builder, Name, [], fun(_) -> {ok, Constructor} end}.
-file("src/pedantic/decode.gleam", 452).
?DOC(" Applies a field description and decoder to the object builder.\n").
-spec field(object_builder(fun((DQV) -> DQW), DQX), binary(), decoder(DQV)) -> object_builder(DQW, DQX).
field(Builder, Name, Field_decoder) ->
Fields = lists:append(
erlang:element(3, Builder),
[{type_field, Name, erlang:element(2, Field_decoder)}]
),
Decoder = fun(Dyn) ->
Constructor_res = (erlang:element(4, Builder))(Dyn),
Field_dyn_res = gleam@dynamic@decode:run(
Dyn,
gleam@dynamic@decode:at(
[Name],
{decoder, fun gleam@dynamic@decode:decode_dynamic/1}
)
),
Is_field_optional = case erlang:element(2, Field_decoder) of
{option, _} ->
true;
{annotated, {option, _}, _} ->
true;
_ ->
false
end,
case {Constructor_res, Field_dyn_res} of
{{ok, Constructor_fn}, {ok, Field_dyn}} ->
case (erlang:element(3, Field_decoder))(Field_dyn) of
{ok, Field_val} ->
{ok, Constructor_fn(Field_val)};
{error, Field_report} ->
{error,
pedantic@report:prefix_path(
Field_report,
{field, Name}
)}
end;
{{ok, Constructor_fn@1}, {error, _}} ->
case Is_field_optional of
true ->
case (erlang:element(3, Field_decoder))(
gleam@dynamic:nil()
) of
{ok, Field_val@1} ->
{ok, Constructor_fn@1(Field_val@1)};
{error, Field_report@1} ->
{error,
pedantic@report:prefix_path(
Field_report@1,
{field, Name}
)}
end;
false ->
Err = pedantic@report:make_error(
[{field, Name}],
missing_field,
Dyn
),
{error, {report, [Err], []}}
end;
{{error, Builder_report}, {ok, Field_dyn@1}} ->
case (erlang:element(3, Field_decoder))(Field_dyn@1) of
{ok, _} ->
{error, Builder_report};
{error, Field_report@2} ->
Prefixed = pedantic@report:prefix_path(
Field_report@2,
{field, Name}
),
{error, pedantic@report:merge(Builder_report, Prefixed)}
end;
{{error, Builder_report@1}, {error, _}} ->
case Is_field_optional of
true ->
{error, Builder_report@1};
false ->
Err@1 = pedantic@report:make_error(
[{field, Name}],
missing_field,
Dyn
),
Field_report@3 = {report, [Err@1], []},
{error,
pedantic@report:merge(
Builder_report@1,
Field_report@3
)}
end
end
end,
{object_builder, erlang:element(2, Builder), Fields, Decoder}.
-file("src/pedantic/decode.gleam", 526).
?DOC(" Declares an optional key in the object builder, resolving to Option(t).\n").
-spec key_optional(
object_builder(fun((gleam@option:option(DRD)) -> DRF), DRG),
binary(),
decoder(DRD)
) -> object_builder(DRF, DRG).
key_optional(Builder, Name, Field_decoder) ->
field(Builder, Name, optional(Field_decoder)).
-file("src/pedantic/decode.gleam", 535).
?DOC(" Declares an optional key with fallback default value.\n").
-spec key_with_default(
object_builder(fun((DRM) -> DRN), DRO),
binary(),
decoder(DRM),
DRM
) -> object_builder(DRN, DRO).
key_with_default(Builder, Name, Field_decoder, Default_val) ->
field(
Builder,
Name,
begin
_pipe = optional(Field_decoder),
default(_pipe, Default_val)
end
).
-file("src/pedantic/decode.gleam", 545).
?DOC(" Finalizes the ObjectBuilder, converting it into a full Decoder.\n").
-spec build(object_builder(DRU, DRU)) -> decoder(DRU).
build(Builder) ->
Function = fun(Dyn) ->
case gleam@dynamic@decode:run(
Dyn,
gleam@dynamic@decode:dict(
{decoder, fun gleam@dynamic@decode:decode_dynamic/1},
{decoder, fun gleam@dynamic@decode:decode_dynamic/1}
)
) of
{ok, _} ->
(erlang:element(4, Builder))(Dyn);
{error, _} ->
Err = pedantic@report:make_error(
[],
{wrong_type,
<<"Object"/utf8>>,
gleam_stdlib:classify_dynamic(Dyn)},
Dyn
),
{error, {report, [Err], []}}
end
end,
{decoder,
{object, erlang:element(2, Builder), erlang:element(3, Builder)},
Function}.
-file("src/pedantic/decode.gleam", 569).
?DOC(" Custom error formatter decorator.\n").
-spec with_error_formatter(
decoder(DRY),
fun((gleam@dynamic:dynamic_(), pedantic@report:error_kind()) -> binary())
) -> decoder(DRY).
with_error_formatter(Decoder, Formatter) ->
Function = fun(Dyn) -> _pipe = (erlang:element(3, Decoder))(Dyn),
gleam@result:map_error(
_pipe,
fun(Rep) ->
Errors = gleam@list:map(
erlang:element(2, Rep),
fun(Err) ->
{error,
erlang:element(2, Err),
Formatter(
erlang:element(5, Err),
erlang:element(4, Err)
),
erlang:element(4, Err),
erlang:element(5, Err)}
end
),
{report, Errors, erlang:element(3, Rep)}
end
) end,
{decoder, erlang:element(2, Decoder), Function}.
-file("src/pedantic/decode.gleam", 587).
?DOC(" Exposes simple custom validation string messages.\n").
-spec with_error_message(decoder(DSB), binary()) -> decoder(DSB).
with_error_message(Decoder, Msg) ->
with_error_formatter(Decoder, fun(_, _) -> Msg end).
-file("src/pedantic/decode.gleam", 592).
?DOC(" Placeholder template parser replacing {value}, {min}, {max}, {length}.\n").
-spec with_template(decoder(DSE), binary()) -> decoder(DSE).
with_template(Decoder, Template) ->
Formatter = fun(Actual, Kind) ->
Val_str = case gleam@dynamic@decode:run(
Actual,
{decoder, fun gleam@dynamic@decode:decode_string/1}
) of
{ok, S} ->
S;
{error, _} ->
case gleam@dynamic@decode:run(
Actual,
{decoder, fun gleam@dynamic@decode:decode_int/1}
) of
{ok, I} ->
erlang:integer_to_binary(I);
{error, _} ->
case gleam@dynamic@decode:run(
Actual,
{decoder, fun gleam@dynamic@decode:decode_float/1}
) of
{ok, F} ->
gleam_stdlib:float_to_string(F);
{error, _} ->
case gleam@dynamic@decode:run(
Actual,
{decoder,
fun gleam@dynamic@decode:decode_bool/1}
) of
{ok, true} ->
<<"true"/utf8>>;
{ok, false} ->
<<"false"/utf8>>;
{error, _} ->
<<"dynamic"/utf8>>
end
end
end
end,
Length_str = begin
_pipe = string:length(Val_str),
erlang:integer_to_binary(_pipe)
end,
Res = begin
_pipe@1 = Template,
_pipe@2 = gleam@string:replace(_pipe@1, <<"{value}"/utf8>>, Val_str),
gleam@string:replace(_pipe@2, <<"{length}"/utf8>>, Length_str)
end,
case Kind of
{too_small, Lim} ->
gleam@string:replace(
Res,
<<"{min}"/utf8>>,
erlang:integer_to_binary(Lim)
);
{too_short, Lim} ->
gleam@string:replace(
Res,
<<"{min}"/utf8>>,
erlang:integer_to_binary(Lim)
);
{too_large, Lim@1} ->
gleam@string:replace(
Res,
<<"{max}"/utf8>>,
erlang:integer_to_binary(Lim@1)
);
{too_long, Lim@1} ->
gleam@string:replace(
Res,
<<"{max}"/utf8>>,
erlang:integer_to_binary(Lim@1)
);
_ ->
Res
end
end,
with_error_formatter(Decoder, Formatter).
-file("src/pedantic/decode.gleam", 638).
?DOC(" Refines a decoder with a custom validation rule.\n").
-spec refine(
decoder(DSH),
fun((DSH) -> {ok, DSH} | {error, pedantic@report:error_kind()})
) -> decoder(DSH).
refine(Decoder, Rule) ->
Function = fun(Dyn) -> case (erlang:element(3, Decoder))(Dyn) of
{ok, Val} ->
case Rule(Val) of
{ok, Refined_val} ->
{ok, Refined_val};
{error, Err_kind} ->
Err = pedantic@report:make_error([], Err_kind, Dyn),
{error, {report, [Err], []}}
end;
{error, Rep} ->
{error, Rep}
end end,
{decoder, erlang:element(2, Decoder), Function}.
-file("src/pedantic/decode.gleam", 660).
?DOC(" Asserts validation constraints using a boolean predicate function.\n").
-spec assert_that(
decoder(DSM),
fun((DSM) -> boolean()),
pedantic@report:error_kind()
) -> decoder(DSM).
assert_that(Decoder, Predicate, Error) ->
refine(Decoder, fun(Val) -> case Predicate(Val) of
true ->
{ok, Val};
false ->
{error, Error}
end end).
-file("src/pedantic/decode.gleam", 674).
?DOC(" Standard check validator.\n").
-spec check(decoder(DSP), fun((DSP) -> boolean()), binary()) -> decoder(DSP).
check(Decoder, Predicate, Msg) ->
assert_that(Decoder, Predicate, {invalid_format, Msg}).
-file("src/pedantic/decode.gleam", 683).
?DOC(" Restricts an Int decoder to values greater than or equal to the minimum.\n").
-spec min(decoder(integer()), integer()) -> decoder(integer()).
min(Decoder, Minimum) ->
refine(Decoder, fun(Val) -> case Val >= Minimum of
true ->
{ok, Val};
false ->
{error, {too_small, Minimum}}
end end).
-file("src/pedantic/decode.gleam", 693).
?DOC(" Restricts an Int decoder to values less than or equal to the maximum.\n").
-spec max(decoder(integer()), integer()) -> decoder(integer()).
max(Decoder, Maximum) ->
refine(Decoder, fun(Val) -> case Val =< Maximum of
true ->
{ok, Val};
false ->
{error, {too_large, Maximum}}
end end).
-file("src/pedantic/decode.gleam", 703).
?DOC(" Restricts a String decoder to a minimum length.\n").
-spec min_length(decoder(binary()), integer()) -> decoder(binary()).
min_length(Decoder, Minimum) ->
refine(Decoder, fun(Val) -> case string:length(Val) >= Minimum of
true ->
{ok, Val};
false ->
{error, {too_short, Minimum}}
end end).
-file("src/pedantic/decode.gleam", 713).
?DOC(" Restricts a String decoder to a maximum length.\n").
-spec max_length(decoder(binary()), integer()) -> decoder(binary()).
max_length(Decoder, Maximum) ->
refine(Decoder, fun(Val) -> case string:length(Val) =< Maximum of
true ->
{ok, Val};
false ->
{error, {too_long, Maximum}}
end end).
-file("src/pedantic/decode.gleam", 724).
-spec positive(decoder(integer())) -> decoder(integer()).
positive(Decoder) ->
refine(Decoder, fun(Val) -> case Val > 0 of
true ->
{ok, Val};
false ->
{error, {too_small, 1}}
end end).
-file("src/pedantic/decode.gleam", 733).
-spec negative(decoder(integer())) -> decoder(integer()).
negative(Decoder) ->
refine(Decoder, fun(Val) -> case Val < 0 of
true ->
{ok, Val};
false ->
{error, {too_large, -1}}
end end).
-file("src/pedantic/decode.gleam", 742).
-spec non_negative(decoder(integer())) -> decoder(integer()).
non_negative(Decoder) ->
refine(Decoder, fun(Val) -> case Val >= 0 of
true ->
{ok, Val};
false ->
{error, {too_small, 0}}
end end).
-file("src/pedantic/decode.gleam", 751).
-spec non_positive(decoder(integer())) -> decoder(integer()).
non_positive(Decoder) ->
refine(Decoder, fun(Val) -> case Val =< 0 of
true ->
{ok, Val};
false ->
{error, {too_large, 0}}
end end).
-file("src/pedantic/decode.gleam", 1079).
-spec map(decoder(DVT), fun((DVT) -> DVV)) -> decoder(DVV).
map(Decoder, Transform_fn) ->
Function = fun(Dyn) -> _pipe = (erlang:element(3, Decoder))(Dyn),
gleam@result:map(_pipe, Transform_fn) end,
{decoder, erlang:element(2, Decoder), Function}.
-file("src/pedantic/decode.gleam", 760).
-spec trim(decoder(binary())) -> decoder(binary()).
trim(Decoder) ->
map(Decoder, fun gleam@string:trim/1).
-file("src/pedantic/decode.gleam", 764).
-spec lowercase(decoder(binary())) -> decoder(binary()).
lowercase(Decoder) ->
map(Decoder, fun string:lowercase/1).
-file("src/pedantic/decode.gleam", 768).
-spec uppercase(decoder(binary())) -> decoder(binary()).
uppercase(Decoder) ->
map(Decoder, fun string:uppercase/1).
-file("src/pedantic/decode.gleam", 896).
-spec is_whitespace(binary()) -> boolean().
is_whitespace(Grapheme) ->
gleam@list:contains(
[<<" "/utf8>>,
<<"\t"/utf8>>,
<<"\n"/utf8>>,
<<"\r"/utf8>>,
<<"\x{00a0}"/utf8>>],
Grapheme
).
-file("src/pedantic/decode.gleam", 846).
-spec collapse_whitespace_string(binary()) -> binary().
collapse_whitespace_string(Value) ->
_pipe = Value,
_pipe@1 = gleam@string:to_graphemes(_pipe),
_pipe@2 = gleam@list:fold(
_pipe@1,
{[], false},
fun(Acc, Grapheme) ->
{Output, Pending_space} = Acc,
case is_whitespace(Grapheme) of
true ->
{Output, not gleam@list:is_empty(Output)};
false ->
Next_output = case Pending_space of
true ->
[Grapheme, <<" "/utf8>> | Output];
false ->
[Grapheme | Output]
end,
{Next_output, false}
end
end
),
(fun(Result) -> _pipe@3 = erlang:element(1, Result),
_pipe@4 = lists:reverse(_pipe@3),
gleam@string:join(_pipe@4, <<""/utf8>>) end)(_pipe@2).
-file("src/pedantic/decode.gleam", 773).
?DOC(" Replaces each run of whitespace with a single space and trims the result.\n").
-spec collapse_whitespace(decoder(binary())) -> decoder(binary()).
collapse_whitespace(Decoder) ->
map(Decoder, fun collapse_whitespace_string/1).
-file("src/pedantic/decode.gleam", 778).
?DOC(" Replaces every occurrence of `pattern` with `replacement`.\n").
-spec replace(decoder(binary()), binary(), binary()) -> decoder(binary()).
replace(Decoder, Pattern, Replacement) ->
map(
Decoder,
fun(Value) -> gleam@string:replace(Value, Pattern, Replacement) end
).
-file("src/pedantic/decode.gleam", 865).
-spec filter_chars(binary(), binary(), boolean()) -> binary().
filter_chars(Value, Characters, Keep_matching) ->
Allowed = gleam@string:to_graphemes(Characters),
_pipe = Value,
_pipe@1 = gleam@string:to_graphemes(_pipe),
_pipe@2 = gleam@list:filter(
_pipe@1,
fun(Grapheme) ->
Matches = gleam@list:contains(Allowed, Grapheme),
case Keep_matching of
true ->
Matches;
false ->
not Matches
end
end
),
gleam@string:join(_pipe@2, <<""/utf8>>).
-file("src/pedantic/decode.gleam", 787).
?DOC(" Removes every grapheme contained in `characters`.\n").
-spec remove_chars(decoder(binary()), binary()) -> decoder(binary()).
remove_chars(Decoder, Characters) ->
map(Decoder, fun(Value) -> filter_chars(Value, Characters, false) end).
-file("src/pedantic/decode.gleam", 795).
?DOC(" Keeps only graphemes contained in `characters`.\n").
-spec allow_chars(decoder(binary()), binary()) -> decoder(binary()).
allow_chars(Decoder, Characters) ->
map(Decoder, fun(Value) -> filter_chars(Value, Characters, true) end).
-file("src/pedantic/decode.gleam", 883).
-spec strip_control_chars_string(binary()) -> binary().
strip_control_chars_string(Value) ->
_pipe = Value,
_pipe@1 = gleam@string:to_utf_codepoints(_pipe),
_pipe@2 = gleam@list:filter(
_pipe@1,
fun(Codepoint) ->
Number = gleam_stdlib:identity(Codepoint),
case (Number < 32) orelse (Number =:= 127) of
true ->
false;
false ->
true
end
end
),
gleam_stdlib:utf_codepoint_list_to_string(_pipe@2).
-file("src/pedantic/decode.gleam", 803).
?DOC(" Removes Unicode control code points from the string.\n").
-spec strip_control_chars(decoder(binary())) -> decoder(binary()).
strip_control_chars(Decoder) ->
map(Decoder, fun strip_control_chars_string/1).
-file("src/pedantic/decode.gleam", 808).
?DOC(" Trims and lowercases a string, useful for case-insensitive identifiers.\n").
-spec normalize_email(decoder(binary())) -> decoder(binary()).
normalize_email(Decoder) ->
map(Decoder, fun(Value) -> _pipe = Value,
_pipe@1 = gleam@string:trim(_pipe),
string:lowercase(_pipe@1) end).
-file("src/pedantic/decode.gleam", 815).
?DOC(" Trims and lowercases a UUID string.\n").
-spec normalize_uuid(decoder(binary())) -> decoder(binary()).
normalize_uuid(Decoder) ->
map(Decoder, fun(Value) -> _pipe = Value,
_pipe@1 = gleam@string:trim(_pipe),
string:lowercase(_pipe@1) end).
-file("src/pedantic/decode.gleam", 822).
?DOC(" Trims a string and replaces blank values with `default_value`.\n").
-spec default_if_blank(decoder(binary()), binary()) -> decoder(binary()).
default_if_blank(Decoder, Default_value) ->
map(
Decoder,
fun(Value) ->
Trimmed = gleam@string:trim(Value),
case gleam@string:is_empty(Trimmed) of
true ->
Default_value;
false ->
Trimmed
end
end
).
-file("src/pedantic/decode.gleam", 836).
?DOC(" Trims a string and turns blank values into `None`.\n").
-spec empty_as_none(decoder(binary())) -> decoder(gleam@option:option(binary())).
empty_as_none(Decoder) ->
map(
Decoder,
fun(Value) ->
Trimmed = gleam@string:trim(Value),
case gleam@string:is_empty(Trimmed) of
true ->
none;
false ->
{some, Trimmed}
end
end
).
-file("src/pedantic/decode.gleam", 900).
-spec length(decoder(binary()), integer()) -> decoder(binary()).
length(Decoder, Len) ->
refine(
Decoder,
fun(Val) ->
Actual_len = string:length(Val),
case Actual_len =:= Len of
true ->
{ok, Val};
false ->
{error,
{invalid_format,
<<"Length must be exactly "/utf8,
(erlang:integer_to_binary(Len))/binary>>}}
end
end
).
-file("src/pedantic/decode.gleam", 913).
-spec is_valid_email(binary()) -> boolean().
is_valid_email(S) ->
gleam_stdlib:contains_string(S, <<"@"/utf8>>) andalso gleam_stdlib:contains_string(
S,
<<"."/utf8>>
).
-file("src/pedantic/decode.gleam", 917).
-spec email(decoder(binary()), binary()) -> decoder(binary()).
email(Decoder, Msg) ->
check(Decoder, fun is_valid_email/1, Msg).
-file("src/pedantic/decode.gleam", 921).
-spec is_valid_uuid(binary()) -> boolean().
is_valid_uuid(S) ->
Segments = gleam@string:split(S, <<"-"/utf8>>),
case gleam@list:map(Segments, fun string:length/1) of
[8, 4, 4, 4, 12] ->
true;
_ ->
false
end.
-file("src/pedantic/decode.gleam", 929).
-spec uuid(decoder(binary()), binary()) -> decoder(binary()).
uuid(Decoder, Msg) ->
check(Decoder, fun is_valid_uuid/1, Msg).
-file("src/pedantic/decode.gleam", 935).
-spec any_of(list(decoder(DUN))) -> decoder(DUN).
any_of(Decoders) ->
Function = fun(Dyn) ->
Result = gleam@list:find_map(
Decoders,
fun(Dec) -> case (erlang:element(3, Dec))(Dyn) of
{ok, Val} ->
{ok, Val};
{error, _} ->
{error, nil}
end end
),
case Result of
{ok, Val@1} ->
{ok, Val@1};
{error, _} ->
Err = pedantic@report:make_error(
[],
{wrong_type,
<<"Union"/utf8>>,
gleam_stdlib:classify_dynamic(Dyn)},
Dyn
),
{error, {report, [Err], []}}
end
end,
{decoder,
{union, gleam@list:map(Decoders, fun(D) -> erlang:element(2, D) end)},
Function}.
-file("src/pedantic/decode.gleam", 966).
-spec min_size(decoder(list(DUR)), integer()) -> decoder(list(DUR)).
min_size(Decoder, Minimum) ->
refine(Decoder, fun(Val) -> case erlang:length(Val) >= Minimum of
true ->
{ok, Val};
false ->
{error, {too_small, Minimum}}
end end).
-file("src/pedantic/decode.gleam", 975).
-spec max_size(decoder(list(DUW)), integer()) -> decoder(list(DUW)).
max_size(Decoder, Maximum) ->
refine(Decoder, fun(Val) -> case erlang:length(Val) =< Maximum of
true ->
{ok, Val};
false ->
{error, {too_large, Maximum}}
end end).
-file("src/pedantic/decode.gleam", 986).
-spec int_range(integer(), integer()) -> decoder(integer()).
int_range(Minimum, Maximum) ->
_pipe = {decoder, primitive_int, fun decode_int/1},
_pipe@1 = min(_pipe, Minimum),
max(_pipe@1, Maximum).
-file("src/pedantic/decode.gleam", 992).
-spec float_range(float(), float()) -> decoder(float()).
float_range(Minimum, Maximum) ->
refine(
{decoder, primitive_float, fun decode_float/1},
fun(Val) -> case (Val >= Minimum) andalso (Val =< Maximum) of
true ->
{ok, Val};
false ->
{error,
{invalid_format, <<"Value must be within range"/utf8>>}}
end end
).
-file("src/pedantic/decode.gleam", 1001).
-spec string_limit(integer(), integer()) -> decoder(binary()).
string_limit(Minimum, Maximum) ->
_pipe = {decoder, primitive_string, fun decode_string/1},
_pipe@1 = min_length(_pipe, Minimum),
max_length(_pipe@1, Maximum).
-file("src/pedantic/decode.gleam", 1012).
-spec decode_non_empty_string(gleam@dynamic:dynamic_()) -> {ok, binary()} |
{error, pedantic@report:report()}.
decode_non_empty_string(Dyn) ->
case (erlang:element(3, {decoder, primitive_string, fun decode_string/1}))(
Dyn
) of
{ok, S} ->
case string:length(S) >= 1 of
true ->
{ok, S};
false ->
Err = pedantic@report:make_error([], {too_short, 1}, Dyn),
{error, {report, [Err], []}}
end;
{error, E} ->
{error, E}
end.
-file("src/pedantic/decode.gleam", 1027).
-spec non_empty_list(decoder(DVG)) -> decoder(list(DVG)).
non_empty_list(Element_decoder) ->
_pipe = list(Element_decoder),
min_size(_pipe, 1).
-file("src/pedantic/decode.gleam", 1037).
?DOC(" Sets the title metadata in the AST graph.\n").
-spec title(decoder(DVK), binary()) -> decoder(DVK).
title(Decoder, Name) ->
New_graph = case erlang:element(2, Decoder) of
{annotated, Inner, Meta} ->
{annotated,
Inner,
{metadata,
{some, Name},
erlang:element(3, Meta),
erlang:element(4, Meta)}};
Other ->
{annotated, Other, {metadata, {some, Name}, none, none}}
end,
{decoder, New_graph, erlang:element(3, Decoder)}.
-file("src/pedantic/decode.gleam", 1051).
?DOC(" Sets the description metadata in the AST graph.\n").
-spec describe(decoder(DVN), binary()) -> decoder(DVN).
describe(Decoder, Desc) ->
New_graph = case erlang:element(2, Decoder) of
{annotated, Inner, Meta} ->
{annotated,
Inner,
{metadata,
erlang:element(2, Meta),
{some, Desc},
erlang:element(4, Meta)}};
Other ->
{annotated, Other, {metadata, none, {some, Desc}, none}}
end,
{decoder, New_graph, erlang:element(3, Decoder)}.
-file("src/pedantic/decode.gleam", 1065).
?DOC(" Sets the example metadata in the AST graph.\n").
-spec example(decoder(DVQ), gleam@dynamic:dynamic_()) -> decoder(DVQ).
example(Decoder, Ex) ->
New_graph = case erlang:element(2, Decoder) of
{annotated, Inner, Meta} ->
{annotated,
Inner,
{metadata,
erlang:element(2, Meta),
erlang:element(3, Meta),
{some, Ex}}};
Other ->
{annotated, Other, {metadata, none, none, {some, Ex}}}
end,
{decoder, New_graph, erlang:element(3, Decoder)}.