package JSON::Validator::Error;
use Mojo::Base -base;
use overload q("") => \&to_string, bool => sub {1}, fallback => 1;
our $MESSAGES = {
allOf => {type => '/allOf Expected %3 - got %4.'},
anyOf => {type => '/anyOf Expected %3 - got %4.'},
array => {
additionalItems => 'Invalid number of items: %3/%4.',
maxContains => 'Contains too many items: %3/%4.',
maxItems => 'Too many items: %3/%4.',
minContains => 'Contains not enough items: %3/%4.',
minItems => 'Not enough items: %3/%4.',
uniqueItems => 'Unique items required.',
contains => 'No items contained.',
},
const => {const => 'Does not match const: %3.'},
enum => {enum => 'Not in enum list: %3.'},
integer => {
ex_maximum => '%3 >= maximum(%4)',
ex_minimum => '%3 <= minimum(%4)',
maximum => '%3 > maximum(%4)',
minimum => '%3 < minimum(%4)',
multipleOf => 'Not multiple of %3.',
},
not => {not => 'Should not match.'},
null => {type => 'Not null.'},
number => {
ex_maximum => '%3 >= maximum(%4)',
ex_minimum => '%3 <= minimum(%4)',
maximum => '%3 > maximum(%4)',
minimum => '%3 < minimum(%4)',
multipleOf => 'Not multiple of %3.',
},
object => {
additionalProperties => 'Properties not allowed: %3.',
maxProperties => 'Too many properties: %3/%4.',
minProperties => 'Not enough properties: %3/%4.',
required => 'Missing property.',
dependencies => 'Missing property. Dependee: %3.',
},
oneOf => {
all_rules_match => 'All of the oneOf rules match.',
n_rules_match => 'oneOf rules %3 match.',
type => '/oneOf Expected %3 - got %4.',
},
string => {
pattern => 'String does not match %3.',
maxLength => 'String is too long: %3/%4.',
minLength => 'String is too short: %3/%4.',
}
};
has details => sub { [qw(generic generic)] };
has message => sub {
my $self = shift;
my $details = $self->details;
my $message;
if (($details->[0] || '') eq 'format') {
$message = '%3';
}
elsif (($details->[1] || '') eq 'type' and @$details == 3) {
$message = 'Expected %1 - got %3.';
}
elsif (my $group = $MESSAGES->{$details->[0]}) {
$message = $group->{$details->[1] || 'default'};
}
return join ' ', Failed => @$details unless defined $message;
$message =~ s!\%(\d)\b!{$details->[$1 - 1] // ''}!ge;
return $message;
};
has path => '/';
sub new {
my $class = shift;
return $class->SUPER::new unless @_;
# Constructed with attributes
return $class->SUPER::new($_[0]) if ref $_[0] eq 'HASH';
# Constructed with ($path, ...)
my $self = $class->SUPER::new;
my $path = ref $_[0] ? join '/', '', map { s!~!~0!g; s!/!~1!g; $_ } @{shift(@_)} : shift || '/';
$self->{path} = $path || '/';
# Constructed with ($path, $message) or ($path, \@details)
return !@_ ? $self : ref $_[0] ? $self->details(shift) : $self->message(shift);
}
sub to_string { sprintf '%s: %s', $_[0]->path, $_[0]->message }
sub TO_JSON { {message => $_[0]->message, path => $_[0]->path} }
1;
=encoding utf8
=head1 NAME
JSON::Validator::Error - JSON::Validator error object
=head1 SYNOPSIS
use JSON::Validator::Error;
my $err = JSON::Validator::Error->new($path, $message);
=head1 DESCRIPTION
L<JSON::Validator::Error> is a class representing validation errors from
L<JSON::Validator>.
=head1 ATTRIBUTES
=head2 details
my $error = $error->details(["generic", "generic"]);
my $error = $error->details([qw(array type object)]);
my $error = $error->details([qw(format date-time Invalid)]);
my $array_ref = $error->details;
Details about the error:
=over 2
=item 1.
Often the category of tests that was run. Example values: allOf, anyOf, array,
const, enum, format, integer, not, null, number, object, oneOf and string.
=item 2.
Often the test that failed. Example values: additionalItems,
additionalProperties, const, enum, maxItems, maxLength, maxProperties, maximum,
minItems, minLength. minProperties, minimum, multipleOf, not, null, pattern,
required, type and uniqueItems,
=item 3.
The rest of the list contains parameters for the test that failed. It can be a
plain human-readable string or numbers indicating things such as max/min
values.
=back
=head2 message
my $str = $error->message;
A human readable description of the error. Defaults to being being constructed
from L</details>. See the C<$MESSAGES> variable in the source code for more
details.
As an EXPERIMENTAL hack you can localize C<$JSON::Validator::Error::MESSAGES>
to get i18n support. Example:
sub validate_i18n {
local $JSON::Validator::Error::MESSAGES = {
allOf => {type => '/allOf Forventet %3 - fikk %4.'},
};
my @error_norwegian = $jv->validate({age => 42});
}
Note that the error messages might contain a mix of English and the local
language. Run some tests to see how it looks.
=head2 path
my $str = $error->path;
A JSON pointer to where the error occurred. Defaults to "/".
=head1 METHODS
=head2 new
my $error = JSON::Validator::Error->new(\%attributes);
my $error = JSON::Validator::Error->new($path, \@details);
my $error = JSON::Validator::Error->new($path, \@details);
Object constructor.
=head2 to_string
my $str = $error->to_string;
Returns the "path" and "message" part as a string: "$path: $message".
=head1 OPERATORS
L<JSON::Validator::Error> overloads the following operators:
=head2 bool
my $bool = !!$error;
Always true.
=head2 stringify
my $str = "$error";
Alias for L</to_string>.
=head1 SEE ALSO
L<JSON::Validator>.
=cut