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, inline]).
-define(FILEPATH, "src/crossbar.gleam").
-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]).
-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 json_mode() :: array | key_value.
-type cross_bar_error() :: {failed_rule, binary(), binary(), binary()}.
-file("src/crossbar.gleam", 38).
?DOC(" Creates a new `Int` field with the given name and value\n").
-spec int(binary(), integer()) -> crossbar@internal@field:field(integer()).
int(Name, Value) ->
{int_field, Name, Value, []}.
-file("src/crossbar.gleam", 43).
?DOC(" Creates a new `Float` field with the given name and value\n").
-spec float(binary(), float()) -> crossbar@internal@field:field(float()).
float(Name, Value) ->
{float_field, Name, Value, []}.
-file("src/crossbar.gleam", 48).
?DOC(" Creates a new `String` field with the given name and value\n").
-spec string(binary(), binary()) -> crossbar@internal@field:field(binary()).
string(Name, Value) ->
{string_field, Name, Value, []}.
-file("src/crossbar.gleam", 53).
?DOC(" Creates a new `Bool` field with the given name and value\n").
-spec bool(binary(), boolean()) -> crossbar@internal@field:field(boolean()).
bool(Name, Value) ->
{bool_field, Name, Value, []}.
-file("src/crossbar.gleam", 58).
?DOC(" Convenience functions for extracting the value from a String field\n").
-spec string_value(crossbar@internal@field:field(binary())) -> binary().
string_value(Field) ->
case Field of
{string_field, _, Value, _} ->
Value;
_ ->
<<""/utf8>>
end.
-file("src/crossbar.gleam", 66).
?DOC(" Convenience functions for extracting the value from an Int field\n").
-spec int_value(crossbar@internal@field:field(integer())) -> integer().
int_value(Field) ->
case Field of
{int_field, _, Value, _} ->
Value;
_ ->
0
end.
-file("src/crossbar.gleam", 74).
?DOC(" Convenience functions for extracting the value from an Float field\n").
-spec float_value(crossbar@internal@field:field(float())) -> float().
float_value(Field) ->
case Field of
{float_field, _, Value, _} ->
Value;
_ ->
+0.0
end.
-file("src/crossbar.gleam", 82).
?DOC(" Convenience functions for extracting the value from a Bool field\n").
-spec bool_value(crossbar@internal@field:field(boolean())) -> boolean().
bool_value(Field) ->
case Field of
{bool_field, _, Value, _} ->
Value;
_ ->
false
end.
-file("src/crossbar.gleam", 90).
?DOC(" Returns the name of a field\n").
-spec field_name(crossbar@internal@field:field(any())) -> binary().
field_name(Field) ->
erlang:element(2, Field).
-file("src/crossbar.gleam", 95).
?DOC(" Returns the string representation of a rule - this is internally used for error states\n").
-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.
-file("src/crossbar.gleam", 133).
-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
).
-file("src/crossbar.gleam", 116).
?DOC(" Get the default error message for a rule - this is internally used for error states\n").
-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_stdlib:float_to_string(V))/binary>>;
{max_size, V@1} ->
<<"must not be greater than "/utf8,
(gleam_stdlib:float_to_string(V@1))/binary>>;
{min_length, V@2} ->
<<<<"must be at least "/utf8,
(erlang:integer_to_binary(V@2))/binary>>/binary,
" characters"/utf8>>;
{max_length, V@3} ->
<<<<"must not be longer than "/utf8,
(erlang:integer_to_binary(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.
-file("src/crossbar.gleam", 140).
-spec append_rule(
crossbar@internal@field:field(EAJ),
crossbar@internal@field:rule(EAJ)
) -> crossbar@internal@field:field(EAJ).
append_rule(Field, Rule) ->
case Field of
{int_field, Name, Value, Rules} ->
{int_field, Name, Value, lists:append(Rules, [Rule])};
{float_field, Name@1, Value@1, Rules@1} ->
{float_field, Name@1, Value@1, lists:append(Rules@1, [Rule])};
{string_field, Name@2, Value@2, Rules@2} ->
{string_field, Name@2, Value@2, lists:append(Rules@2, [Rule])};
{bool_field, Name@3, Value@3, Rules@3} ->
{bool_field, Name@3, Value@3, lists:append(Rules@3, [Rule])}
end.
-file("src/crossbar.gleam", 155).
-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, erlang:float(Value)};
{not_eq, Name@1, Value@1} ->
{not_eq, Name@1, erlang: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 = erlang:round(_pipe@1),
Original_validator(_pipe@2) end, Error@1}
end end).
-file("src/crossbar.gleam", 189).
?DOC(
" Convenient function to convert an `Int` to a `Float`, you should use this BEFORE applying any rules.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" int(\"age\", 6)\n"
" |> to_float\n"
" |> min_value(5.0)\n"
" ```\n"
).
-spec to_float(crossbar@internal@field:field(integer())) -> crossbar@internal@field:field(float()).
to_float(Field) ->
{Name@1, Value@1, Rules@1} = case Field of
{int_field, Name, Value, Rules} -> {Name, Value, Rules};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"crossbar"/utf8>>,
function => <<"to_float"/utf8>>,
line => 190,
value => _assert_fail,
start => 5590,
'end' => 5637,
pattern_start => 5601,
pattern_end => 5629})
end,
_pipe = Value@1,
_pipe@1 = erlang:float(_pipe),
{float_field, Name@1, _pipe@1, int_rules_to_float_rules(Rules@1)}.
-file("src/crossbar.gleam", 206).
?DOC(
" The required rule makes sure that the field is not empty, this is the expected behaviour in the following cases:\n"
" > `Int`: **0** is considered empty\n"
"\n"
" > `Float`: **0.0** is also considered empty\n"
"\n"
" > `String`: \"\" (or anything that trims down to that) is considered empty\n"
"\n"
" > `Bool`: this isn't really a thing, but it's here for completeness sake and it will always return true, because a bool is never empty (unless it is wrapped in an option)\n"
).
-spec required(crossbar@internal@field:field(EGG)) -> crossbar@internal@field:field(EGG).
required(Field) ->
append_rule(Field, required).
-file("src/crossbar.gleam", 214).
?DOC(
" The `min_value` rule makes sure that the field is at least the given (byte where it applies) size.\n"
" > Strings are counted in bytes (as bit arrays), `Int`s and `Float`s are evaluated directly, `Bool`s are treated as their binary equivalent (0 or 1).\n"
"\n"
" NOTE: This function has been momentarily restricted to `Float` fields, because it's not very useful for other types, open an issue if you ever find a use for it. There is also a `to_float` function to transform int fields to float fields (meant to be used before you add any rules)\n"
).
-spec min_value(crossbar@internal@field:field(float()), float()) -> crossbar@internal@field:field(float()).
min_value(Field, Size) ->
append_rule(Field, {min_size, Size}).
-file("src/crossbar.gleam", 222).
?DOC(
" The `max_value` rule makes sure that the field is at most the given (byte) size.\n"
" > Strings are counted in bytes (as bit arrays), `Int`s and `Float`s are evaluated directly, `Bool`s are treated as their binary equivalent (0 or 1).\n"
"\n"
" NOTE: This function has been momentarily restricted to `Float` fields, because it's not very useful for other types, open an issue if you ever find a use for it. There is also a `to_float` function to transform int fields to float fields (meant to be used before you add any rules)\n"
).
-spec max_value(crossbar@internal@field:field(float()), float()) -> crossbar@internal@field:field(float()).
max_value(Field, Size) ->
append_rule(Field, {max_size, Size}).
-file("src/crossbar.gleam", 236).
?DOC(
" The `min_length` rule makes sure that the field is at least the given length, this is the expected behaviour in the following cases:\n"
" > `Int`: the length of the string representation of the number, this isn't very useful to you, but it's here for completeness sake. You probably want to use `min_value` instead.\n"
"\n"
" > `Float`: the length of the string representation of the number, this isn't very useful to you, but it's here for completeness sake. You probably want to use `min_value` instead.\n"
"\n"
" > `String`: the length of the string is evaluated directly\n"
"\n"
" > `Bool`: this also isn't very useful to you, but it's here for completeness sake.\n"
"\n"
" NOTE: This function has been momentarily restricted to `String` fields, because it's not very useful for other types, open an issue if you ever find a use for it.\n"
).
-spec min_length(crossbar@internal@field:field(binary()), integer()) -> crossbar@internal@field:field(binary()).
min_length(Field, Length) ->
append_rule(Field, {min_length, Length}).
-file("src/crossbar.gleam", 250).
?DOC(
" The `max_length` rule makes sure that the field is at most the given length, this is the expected behaviour in the following cases:\n"
" > `Int`: the length of the string representation of the number, this isn't very useful to you, but it's here for completeness sake. You probably want to use `max_value` instead.\n"
"\n"
" > `Float`: the length of the string representation of the number, this also isn't very useful to you, but it's here for completeness sake. You probably want to use `max_value` instead.\n"
"\n"
" > `String`: the length of the string is evaluated directly\n"
"\n"
" > `Bool`: this also isn't very useful to you, but it's here for completeness sake.\n"
"\n"
" NOTE: This function has been momentarily restricted to `String` fields, because it's not very useful for other types, open an issue if you ever find a use for it.\n"
).
-spec max_length(crossbar@internal@field:field(binary()), integer()) -> crossbar@internal@field:field(binary()).
max_length(Field, Length) ->
append_rule(Field, {max_length, Length}).
-file("src/crossbar.gleam", 255).
?DOC(" The `eq` rule makes sure that the field is equal to the given value, strings are NOT compared securely, so this is NOT safe to use for passwords, all types are compared directly.\n").
-spec eq(crossbar@internal@field:field(EBJ), binary(), EBJ) -> crossbar@internal@field:field(EBJ).
eq(Field, Name, Value) ->
append_rule(Field, {eq, Name, Value}).
-file("src/crossbar.gleam", 263).
?DOC(" The `not_eq` rule makes sure that the field is not equal to the given value, strings are NOT compared securely, so this is NOT safe to use for passwords, other types are compared directly.\n").
-spec not_eq(crossbar@internal@field:field(EBM), binary(), EBM) -> crossbar@internal@field:field(EBM).
not_eq(Field, Name, Value) ->
append_rule(Field, {not_eq, Name, Value}).
-file("src/crossbar.gleam", 285).
?DOC(
" The `with_validator` rule makes sure that the field passes the given validator function, the function should return a `Bool` and take the field value as its only argument.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let validator = fn(x) { x > 5 }\n"
" let error = \"must be greater than 5\"\n"
"\n"
" int(\"age\", 6)\n"
" |> with_validator(\"greater_than_5\", validator, error)\n"
" ```\n"
).
-spec with_validator(
crossbar@internal@field:field(EBP),
binary(),
fun((EBP) -> boolean()),
binary()
) -> crossbar@internal@field:field(EBP).
with_validator(Field, Name, Func, Error) ->
append_rule(Field, {validator_function, Name, Func, Error}).
-file("src/crossbar.gleam", 307).
?DOC(
" The `regex` rule makes sure that the field matches the given regex, you are required to compile the regex yourself, if you want to use an uncompiled regex, use the `uncompiled_regex` rule instead.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import gleam/regex\n"
"\n"
" let options = Options(case_insensitive: False, multi_line: True)\n"
" let assert Ok(re) = compile(\"^[0-9]\", with: options)\n"
"\n"
" string(\"name\", \"1john\")\n"
" |> regex(\"starts_with_number\", re, \"must start with a number\")\n"
" ```\n"
).
-spec regex(
crossbar@internal@field:field(EBS),
binary(),
gleam@regexp:regexp(),
binary()
) -> crossbar@internal@field:field(EBS).
regex(Field, Name, Regex, Error) ->
append_rule(Field, {regex, Name, Regex, Error}).
-file("src/crossbar.gleam", 317).
?DOC(" Useful for extracting the errors as a list of tuples (#(rule_name, error_as_string)), this is useful for returning errors as JSON.\n").
-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).
-file("src/crossbar.gleam", 330).
-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.
-file("src/crossbar.gleam", 380).
?DOC(
" Transform a field into a tuple that can be used to generate JSON, this is useful for returning errors as JSON. The `field_name` argument is optional, if you don't provide it, the field name will be extracted from the validation result if it can be (which is usually the case).\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let first_name =\n"
" string(\"first_name\", \"\")\n"
" |> required\n"
" |> min_length(3)\n"
" |> validate\n"
" |> to_serializable(\"\", KeyValue)\n"
"\n"
" let last_name =\n"
" string(\"last_name\", \"Smith\")\n"
" |> required\n"
" |> min_length(1)\n"
" |> max_length(3)\n"
" |> validate\n"
" |> to_serializable(\"renamed_last_field\", KeyValue)\n"
"\n"
" json.object([first_name, last_name])\n"
" |> json.to_string\n"
" |> io.println\n"
" ```\n"
"\n"
" The above example will produce the following JSON:\n"
"\n"
" ```json\n"
" {\n"
" \"first_name\": {\n"
" \"required\": \"is required\",\n"
" \"min_length\": \"must be at least 3 characters\"\n"
" },\n"
" \"renamed_last_name\": {\n"
" \"max_length\": \"must not be longer than 3 characters\"\n"
" }\n"
" }\n"
" ```\n"
).
-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 ->
_pipe@1 = Errors,
_pipe@2 = gleam@list:map(
_pipe@1,
fun(E@1) ->
{erlang:element(1, E@1),
gleam@json:string(erlang:element(2, E@1))}
end
),
gleam@json:object(_pipe@2)
end,
{Name, Json_value}
end
).
-file("src/crossbar.gleam", 443).
?DOC(
" Validate a list of fields and transform them into a list of tuples that can be used to generate JSON, this is useful for returning errors as JSON.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let first_name =\n"
" string(\"first_name\", \"\")\n"
" |> required\n"
" |> min_length(3)\n"
"\n"
" let last_name =\n"
" string(\"last_name\", \"Smith\")\n"
" |> required\n"
" |> min_length(1)\n"
" |> max_length(3)\n"
"\n"
" to_serializable_list([first_name, last_name], KeyValue)\n"
" |> serializables_to_string\n"
" |> io.println\n"
" ```\n"
"\n"
" The above example will produce the following JSON:\n"
"\n"
" ```json\n"
" {\n"
" \"first_name\": {\n"
" \"required\": \"is required\",\n"
" \"min_length\": \"must be at least 3 characters\"\n"
" },\n"
" \"last_name\": {\n"
" \"max_length\": \"must not be longer than 3 characters\"\n"
" }\n"
" }\n"
" ```\n"
).
-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
).
-file("src/crossbar.gleam", 454).
?DOC(" Utility function to convert a list of serializable tuples into a JSON string.\n").
-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).
-file("src/crossbar.gleam", 487).
?DOC(
" Useful for checking if the result of `to_serializable` collected into a list has any errors, this is useful for checking if any of the fields failed validation.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let first_name =\n"
" string(\"first_name\", \"John\")\n"
" |> required\n"
" |> min_length(3)\n"
" |> to_serializable(KeyValue)\n"
"\n"
" let last_name =\n"
" string(\"last_name\", \"Smith\")\n"
" |> required\n"
" |> min_length(1)\n"
" |> max_length(10)\n"
" |> to_serializable(KeyValue)\n"
"\n"
" let errors = [first_name, last_name]\n"
"\n"
" case has_errors(errors) {\n"
" True -> io.println(\"There are errors\")\n"
" False -> io.println(\"There are no errors\")\n"
" }\n"
" ```\n"
).
-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).
-file("src/crossbar.gleam", 493).
-spec validate_field(
crossbar@internal@field:field(ECH),
list(crossbar@internal@field:rule(ECH)),
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 = begin
gleam@bool:guard(
Validation_result,
Errors,
fun() -> _pipe = Errors,
lists:append(
_pipe,
[{failed_rule,
erlang:element(2, Field),
rule_to_string(Rule),
rule_to_error_string(Rule)}]
) end
)
end,
validate_field(Field, Other_rules, New_errors);
[] ->
Errors
end.
-file("src/crossbar.gleam", 542).
?DOC(
" Run the validation on the given field, returns an `Ok` if the validation was successful, otherwise returns an `Error` with a list of errors.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let field = int(\"age\", 6)\n"
" |> to_float\n"
" |> min_value(5.0)\n"
" |> validate\n"
" ```\n"
).
-spec validate(crossbar@internal@field:field(ECN)) -> {ok,
crossbar@internal@field:field(ECN)} |
{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.
-file("src/crossbar.gleam", 552).
?DOC(" Validate a list of fields, returns a list of tuples with the field name and a list of errors - unfortunately, currently, only supports validating fields of the same type.\n").
-spec validate_many(list(crossbar@internal@field:field(any())), boolean()) -> list({binary(),
list(cross_bar_error())}).
validate_many(Fields, Failed_only) ->
Results = begin
_pipe = Fields,
gleam@list:map(
_pipe,
fun(Field) ->
Errors = begin
_pipe@1 = validate(Field),
gleam@result:unwrap_error(_pipe@1, [])
end,
{erlang:element(2, Field), Errors}
end
)
end,
gleam@bool:guard(not Failed_only, Results, fun() -> _pipe@2 = Results,
gleam@list:filter(
_pipe@2,
fun(Result) -> erlang:element(2, Result) /= [] end
) end).