### ----------------------------------------------------
### -- JSON::Unmarshal
### -- Licenses: Artistic-2.0
### -- Authors: Tadeusz Sośnierz, Raku Community
### -- File: t/080-trait.rakutest
### ----------------------------------------------------
use Test;

use JSON::Unmarshal;

plan 4;

subtest "unmarshalled-by trait with Code" => {
    class VersionClassCode {
        has Version $.version is unmarshalled-by(-> $v { Version.new($v) });
    }

    my $json = '{ "version" : "0.0.1" }';

    my VersionClassCode $obj;

    lives-ok { $obj = unmarshal($json, VersionClassCode) }, "unmarshall with attrbute strait (code)";
    isa-ok $obj.version, Version, "the attribute is the right kind of thing";
    ok $obj.version.defined, "and it's defined";
    is $obj.version, Version.new("0.0.1"), "and has the right value";
}

subtest "unmarshalled-by trait with Method name" => {
    class VersionClassMethod {
        has Version $.version is unmarshalled-by('new');
    }

    my $json = '{ "version" : "0.0.1" }';

    my VersionClassMethod $obj;

    lives-ok { $obj = unmarshal($json, VersionClassMethod) }, "unmarshall with attrbute trait (method name)";
    isa-ok $obj.version, Version, "the attribute is the right kind of thing";
    ok $obj.version.defined, "and it's defined";
    is $obj.version, Version.new("0.0.1"), "and has the right value";
}

subtest "unmarshalled-by trait with inheritance" => {
    class Inner {
        has Str $.name is required;
    }
    class OuterClass {
        has Inner $.inner;
    }
    class OuterSubClass is OuterClass {
        has Inner $.inner is unmarshalled-by(-> $name { Inner.new(:$name) });
    }

    my $json = '{ "inner" : "name" }';

    my OuterSubClass $obj;

    lives-ok { $obj = unmarshal($json, OuterSubClass) }, "unmarshall with attrbute trait on sub-class";
    isa-ok $obj.inner, Inner, "the attribute is the right kind of thing";
    ok $obj.inner.defined, "and it's defined";
    is $obj.inner.name, "name", "and has the right value";
}

subtest "unmarshalled-by on a positional attribute" => {
    class CustomArrayAttribute {
        class Inner {
            has Str $.name is required;
        }

        sub unmarshall-inners (@inners) {
            @inners.map(-> $name { Inner.new(:$name) })
        }

        has Inner @.inners is unmarshalled-by(&unmarshall-inners);
    }

    my $json = '{ "inners" : [ "one", "two", "three" ] }';
    my $obj;
    lives-ok {
        $obj = unmarshal $json, CustomArrayAttribute;
    }, "unmarshal with custom marshaller on positional attribute";

    # The code below is an expansion of the logic: all($obj.inners) ~~ CustomArrayAttribute::Inner
    # It has been written out in full for compatibility across versions and OSes (looking at you windows).
    {
        my @checked = do for $obj.inners.kv -> $i, $inner {
            ok $inner ~~ CustomArrayAttribute::Inner, "and all the objects in the array are correct (i=$i)";
            $i
        }
        is @checked.tail, $obj.inners.end, "and all array objects were checked";
    }
    
    is-deeply $obj.inners.map( *.name), <one two three>, "and they have their names set correctly";
}

# vim: expandtab shiftwidth=4 ft=raku


