Validate: Rename "scalar" to "accept_scalar" and add "accept_array"

This commit is contained in:
Yorhel 2025-03-16 15:39:10 +01:00
parent f8fe53cba9
commit 3382deba9a
2 changed files with 33 additions and 10 deletions

View file

@ -13,7 +13,8 @@ my %builtin = map +($_,1), qw/
default
onerror
trim
elems scalar sort unique
elems sort unique
accept_scalar accept_array
keys values unknown missing
func
/;
@ -22,8 +23,9 @@ my %type_vals = map +($_,1), qw/scalar hash array any/;
my %unknown_vals = map +($_,1), qw/remove reject pass/;
my %missing_vals = map +($_,1), qw/create reject ignore/;
my %implied_type = qw/
accept_array scalar
keys hash values hash unknown hash
elems array sort array unique array scalar array
elems array sort array unique array accept_scalar array
/;
my %sort_vals = (
str => sub($x,$y) { $x cmp $y },
@ -151,6 +153,7 @@ sub _compile($schema, $custom, $rec, $top, $validations=$top->{validations}) {
if ($builtin{$name}) {
confess "Invalid value for 'missing': $val" if $name eq 'missing' && !$missing_vals{$val};
confess "Invalid value for 'unknown': $val" if $name eq 'unknown' && !$unknown_vals{$val};
confess "Invalid value for 'accept_array': $val" if $name eq 'accept_array' && $val && $val ne 'first' && $val ne 'last';
$val = $sort_vals{$val} || confess "Unknown value for 'sort': $val" if $name eq 'sort' && ref $val ne 'CODE';
$top->{$name} = $val;
next;
@ -273,6 +276,10 @@ sub _validate_input {
my $type = $c->{type} // 'scalar';
# accept_array (needs to be done before 'trim')
$_[1] = $_[1]->@* == 0 ? undef : $c->{accept_array} eq 'first' ? $_[1][0] : $_[1][ $#{$_[1]} ]
if $c->{accept_array} && ref $_[1] eq 'ARRAY';
# trim (needs to be done before the 'default' test)
$_[1] = trim $_[1] =~ s/\r//rg if defined $_[1] && !ref $_[1] && $type eq 'scalar' && (!exists $c->{trim} || $c->{trim});
@ -305,8 +312,8 @@ sub _validate_input {
}
} elsif ($type eq 'array') {
$_[1] = [$_[1]] if $c->{scalar} && !ref $_[1];
return { validation => 'type', expected => $c->{scalar} ? 'array or scalar' : 'array', got => lc ref $_[1] || 'scalar' } if ref $_[1] ne 'ARRAY';
$_[1] = [$_[1]] if $c->{accept_scalar} && !ref $_[1];
return { validation => 'type', expected => $c->{accept_scalar} ? 'array or scalar' : 'array', got => lc ref $_[1] || 'scalar' } if ref $_[1] ne 'ARRAY';
$_[1] = [$_[1]->@*]; # Create a shallow copy to prevent in-place modification.
} elsif ($type eq 'any') {
@ -600,10 +607,10 @@ Failure is reported in a similar fashion to I<keys>:
]
}
=item scalar => 0/1
=item accept_scalar => 0/1
Implies C<< type => 'array' >>, this option will also permit the input to be a
scalar. In this case, the input is interpreted and returned as an array with
scalar. In that case, the input is interpreted and returned as an array with
only one element. This option exists to make it easy to validate multi-value
form inputs. For example, consider C<query_decode()> in L<FU::Util>: a
parameter in a query string is decoded into an array if it is listed multiple
@ -613,16 +620,24 @@ times, a scalar if it only occcurs once. So we could either end up with:
# OR:
{ a => [1, 3], b => 1 }
With the I<scalar> option, we can accept both forms for C<a> and normalize into
an array. The following schema definition can validate the above examples:
With the I<accept_scalar> option, we can accept both forms for C<a> and
normalize into an array. The following schema definition can validate the above
examples:
{ type => 'hash',
keys => {
a => { type => 'array', scalar => 1 },
a => { type => 'array', accept_scalar => 1 },
b => { }
}
}
=item accept_array => false/'first'/'last'
Implies C<< type => 'scalar' >>. Similar to I<accept_scalar> but normalizes in
the other direction: when the input is an array, only the first or last item is
extracted and the other elements are ignored. If the input is an empty array,
the value is taken to be C<undef>.
=item sort => $option
Implies C<< type => 'array' >>, sort the array after validating its elements.