Data-Sah-Resolve
view release on metacpan or search on metacpan
lib/Data/Sah/Resolve.pm view on Meta::CPAN
package Data::Sah::Resolve;
our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
our $DATE = '2021-07-29'; # DATE
our $DIST = 'Data-Sah-Resolve'; # DIST
our $VERSION = '0.011'; # VERSION
use 5.010001;
use strict;
use warnings;
use Exporter qw(import);
our @EXPORT_OK = qw(resolve_schema);
sub _clset_has_merge {
my $clset = shift;
for (keys %$clset) {
return 1 if /\Amerge\./;
}
0;
}
sub _resolve {
my ($opts, $res) = @_;
my $type = $res->{type};
die "Cannot resolve Sah schema: circular schema definition: ".
join(" -> ", @{$res->{resolve_path}}, $type)
if grep { $type eq $_ } @{$res->{resolve_path}};
unshift @{$res->{resolve_path}}, $type;
# check whether $type is a built-in Sah type
(my $typemod_pm = "Data/Sah/Type/$type.pm") =~ s!::!/!g;
eval { require $typemod_pm; 1 };
my $err = $@;
unless ($err) {
# already a builtin-type, so we stop here
return;
}
die "Cannot resolve Sah schema: can't check whether $type is a builtin Sah type: $err"
unless $err =~ /\ACan't locate/;
# not a type, try a schema under Sah::Schema
my $schmod = "Sah::Schema::$type";
(my $schmod_pm = "$schmod.pm") =~ s!::!/!g;
eval { require $schmod_pm; 1 };
die "Cannot resolve Sah schema: not a known built-in Sah type '$type' (can't locate ".
"Data::Sah::Type::$type) and not a known schema name '$type' ($@)"
if $@;
no strict 'refs';
my $sch2 = ${"$schmod\::schema"};
die "Cannot resolve Sah schema: BUG: Schema module $schmod doesn't contain \$schema"
unless $sch2;
$res->{type} = $sch2->[0];
unshift @{ $res->{clsets_after_type} }, $sch2->[1];
_resolve($opts, $res);
}
sub resolve_schema {
my $opts = ref($_[0]) eq 'HASH' ? shift : {};
my $sch = shift;
# normalize
unless ($opts->{schema_is_normalized}) {
require Data::Sah::Normalize;
$sch = Data::Sah::Normalize::normalize_schema($sch);
}
my $res = {
v => 2,
type => $sch->[0],
clsets_after_type => [$sch->[1]],
resolve_path => [],
};
# resolve
_resolve($opts, $res);
# determine the "base restrictions" base
my @clsets_have_merge;
my $has_merge_prefixes; # whether any of the clsets have merge prefixes
for (@{ $res->{clsets_after_type} }) {
push @clsets_have_merge, _clset_has_merge($_);
$has_merge_prefixes++ if $clsets_have_merge[-1];
}
# TODO: sanity check: the innermost base schema should not have merge prefixes
my $idx = $#clsets_have_merge;
while ($idx >= 0) {
if ($opts->{allow_base_with_no_additional_clauses}) {
last if !$clsets_have_merge[$idx];
} else {
last if keys(%{$res->{clsets_after_type}[$idx]}) > 0 && !$clsets_have_merge[$idx];
}
$idx--;
}
#use DD; dd $res->{clsets_after_type}; dd \@clsets_have_merge;
$res->{base} = $res->{resolve_path}[$idx];
$res->{clsets_after_base} = [grep {keys(%$_) > 0} @{ $res->{clsets_after_type} }[$idx .. $#clsets_have_merge]];
# merge
my @merged_clsets;
MERGE: {
unless ($has_merge_prefixes) {
@merged_clsets = grep { keys(%$_)>0 } @{ $res->{clsets_after_type} };
last;
}
@merged_clsets = ($res->{clsets_after_type}[0]);
for my $i (1 .. $#clsets_have_merge) {
my $clset = $res->{clsets_after_type}[$i];
next unless keys(%$clset) > 0;
if ($clsets_have_merge[$i]) {
state $merger = do {
( run in 3.424 seconds using v1.01-cache-2.11-cpan-140bd7fdf52 )