NAME
    Data::Sah::Resolve - Resolve Sah schema

VERSION
    This document describes version 0.011 of Data::Sah::Resolve (from Perl
    distribution Data-Sah-Resolve), released on 2021-07-29.

SYNOPSIS
     use Data::Sah::Resolve qw(resolve_schema);

     my $sch = resolve_schema("int");
     # => {
     #      v => 2,
     #      type=>"int",
     #      clsets_after_type => [],
     #      "clsets_after_type.alt.merge.merged" => [],
     #      base=>"int",
     #      clsets_after_base => [],
     #      resolve_path => ["int"],
     #    }

     my $sch = resolve_schema("posint*");
     # => {
     #      v => 2,
     #      type=>"int",
     #      clsets_after_type => [{min=>1}, {req=>1}],
     #      "clsets_after_type.alt.merge.merged" => [{min=>1}, {req=>1}],
     #      base => "posint",
     #      clsets_after_base => [{req=>1}],
     #      resolve_path => ["int","posint"],
     #    }

     my $sch = resolve_schema([posint => div_by => 3]);
     # => {
     #      v => 2,
     #      type=>"int",
     #      clsets_after_type => [{min=>1}, {div_by=>3}],
     #      "clsets_after_type.alt.merge.merged" => [{min=>1}, {div_by=>3}],
     #      base => "posint",
     #      clsets_after_base => [{div_by=>3}],
     #      resolve_path => ["int","posint"],
     #    }
     # => ["int", {min=>1}, {div_by=>3}]

     my $sch = resolve_schema(["posint", "merge.delete.min"=>undef, div_by => 3]);
     # basically becomes: ["int", div_by=>3]
     # => {
     #      v => 2,
     #      type=>"int",
     #      clsets_after_type => [{min=>1}, {"merge.delete.min"=>undef, div_by=>3}],
     #      "clsets_after_type.alt.merge.merged" => [{div_by=>3}],
     #      base => undef,
     #      clsets_after_base => [{div_by=>3}],
     #      resolve_path => ["int","posint"],
     #    }
     # => ["int", {min=>1}, {div_by=>3}]

DESCRIPTION
    This module provides "resolve_schema".

FUNCTIONS
  resolve_schema
    Usage:

     my $res = resolve_schema([ \%opts, ] $sch); # => hash

    Sah schemas can be defined in terms of other schemas as base. The
    resolving process follows the (outermost) base schema until it finds a
    builtin type as the (innermost) base. It then returns a hash result (a
    DefHash with "v"=2) containing the type as well other information like
    the collected clause sets and others.

    This routine performs the following steps:

    1. Normalize the schema
        Unless "schema_is_normalized" option is true, in which case schema
        is assumed to be normalized already.

    2. Check if the schema's type is a builtin type
        Currently this is done by checking if the module of the name
        "Data::Sah::Type::<type>" is loadable. If it is a builtin type then
        we are done.

    3. Check if the schema's type is the name of another schema
        This is done by checking if "Sah::Schema::<name>" module exists and
        is loadable. If this is the case then we retrieve the base schema
        from the $schema variable in the "Sah::Schema::<name>" package and
        repeat the process while accumulating and/or merging the clause
        sets.

    4. If schema's type is neither, we die.

    Will also die on circularity or when there is other failures like
    failing to get schema from the schema module.

    Example 1: "int".

    First we normalize to "["int",{}]". The type is "int" and it is a
    builtin type (Data::Sah::Type::int exists). The final result is:

     {
       v => 2,
       type=>"int",
       clsets_after_type => [],
       "clsets_after_type.alt.merge.unmerged" => [],
       base=>undef,
       clsets_after_base => [],
       resolve_path => ["int"],
     }

    Example 2: "posint*".

    First we normalize to "["posint",{req=>1}]". The type part of this
    schema is "posint" and it is actually the name of another schema because
    "Data::Sah::Type::posint" is not found and we find schema module
    Sah::Schema::posint) instead. We then retrieve the "posint" schema from
    the schema module's $schema and we get "["int", {min=>1}]" (additional
    informative clauses omitted for brevity). We now try to resolve "int"
    and find that it's a builtin type. So the final result is:

     {
       v => 2,
       type=>"int",
       clsets_after_type => [{min=>1}, {req=>1}],
       "clsets_after_type.alt.merge.unmerged" => [{min=>1}, {req=>1}],
       base => "posint",
       clsets_after_base => [{req=>1}],
       resolve_path => ["int","posint"],
     }

    Known options:

    *   schema_is_normalized

        Bool, default false. When set to true, function will skip
        normalizing schema and assume input schema is normalized.

    *   allow_base_with_no_additional_clauses

        Bool, default false. Normally, a schema like "posint" or
        "["posint",{}]" will result in "int" as the base (because the schema
        does not add any additional clauses to the "posint" schema) while
        "["posint",{div_by=>2}]" will result in "posint" as the base. But if
        this setting is set to true, then all the previous examples will
        result in "posint" as the base.

    As mentioned, result is a hash conforming to the DefHash restriction.
    The following keys will be returned:

    *   v

        Integer, has the value of 2. A non-compatible change of result will
        bump this version number.

    *   type

        Str, the Sah builtin type name.

    *   clsets_after_type

        All the collected clause sets, from the deepest base schema to the
        outermost, and to the clause set of the original unresolved schema.

    *   clsets_after_type.alt.merge.merged

        Like "clsets_after_type", but the clause sets are merged according
        to the Sah merging specification.

    *   base

        Str. Might be undef. The outermost base schema (or type) that can be
        used as "base restriction", meaning its restrictions (clause sets)
        must all be fulfilled. After this base's clause sets, the next
        additional clause sets will not contain any merge prefixes. Because
        if additional clause sets contained merge prefixes, they could
        modify or remove restrictions set by the base instead of just adding
        more restrictions (which is the whole point of merging).

    *   clsets_after_base

        Clause sets after the "base restriction" base. This is additional
        restrictions that are imposed to the restrictions of the base
        schema. They do not contain merge prefixes.

    *   resolve_path

        Array. This is a list of schema type names or builtin type names,
        from the deepest to the shallowest. The first element of this
        arrayref is the builtin Sah type and the last element is the
        original unresolved schema's type.

HOMEPAGE
    Please visit the project's homepage at
    <https://metacpan.org/release/Data-Sah-Resolve>.

SOURCE
    Source repository is at
    <https://github.com/perlancar/perl-Data-Sah-Resolve>.

BUGS
    Please report any bugs or feature requests on the bugtracker website
    <https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Sah-Resolve>

    When submitting a bug or request, please include a test-file or a patch
    to an existing test-file that illustrates the bug or desired feature.

SEE ALSO
    Sah, Data::Sah

AUTHOR
    perlancar <perlancar@cpan.org>

COPYRIGHT AND LICENSE
    This software is copyright (c) 2021, 2017, 2016 by perlancar@cpan.org.

    This is free software; you can redistribute it and/or modify it under
    the same terms as the Perl 5 programming language system itself.

