### ----------------------------------------------------
### -- ValueClass
### -- Licenses: Artistic-2.0
### -- Authors: Fernando Corrêa de Oliveira
### -- File: t/01-basic.rakutest
### ----------------------------------------------------
use Test;
use ValueClass;

eval-lives-ok 'use ValueClass; my value-class Bla {}';
eval-dies-ok  'use ValueClass; my value-class Bla is rw {}';
eval-lives-ok 'use ValueClass; my value-class Bla { has $.a }';
eval-dies-ok  'use ValueClass; my value-class Bla { has $.a is rw }';
eval-lives-ok 'use ValueClass; my value-class Bla { has $.a; has $.b; has $.c }';
eval-dies-ok  'use ValueClass; my value-class Bla { has $.a; has $.b is rw; has $.c }';

for
  my value-class :: {
    has $.a;

    method uses-a { $!a }
    method changes-a { $!a = 42 }
  },
  my value-class :: {
    has $.a;
    has $.b;

    method uses-a { $!a }
    method changes-a { $!a = 42 }
  }
-> \Bla {

  lives-ok {
    my $bla = Bla.new;
    isa-ok $bla, Bla;
    is $bla.a, Any;
  }

  lives-ok {
    my $bla = Bla.new: :1a;
    isa-ok $bla, Bla;
    is $bla.a, 1;
    is $bla.uses-a, 1;
  }

  throws-like {
    my $bla = Bla.new: :1a;
    $bla.changes-a;
  }, message => /"Cannot assign to an immutable value"/, X::AdHoc;

  throws-like {
    my $bla = Bla.new: :a[];
  }, message => /"All attributes of value-class ({ Bla.^name }) should be value types"/, X::AdHoc;
}

for
  my value-class :: { has @.a }, [1, 2, 3],
  my value-class :: { has %.a }, %(a => 1, b => 2)
-> \Bla, $a {
  lives-ok {
    Bla.new
  }

  lives-ok {
    Bla.new: :a(|$a)
  }
}

my value-class Bla {
  has Int $.a;
  has Str $.b;

  method gist { $!b x $!a }
}

is Bla.new(:3a, :b<test>).gist, "testtesttest";

my %set := set(Bla.new(:3a, :b<test>) xx 10);

is %set.elems, 1;
ok %set{Bla.new: :3a, :b<test>};
ok !%set{Bla.new: :4a, :b<test>};

my %bag := bag(Bla.new(:3a, :b<test>) xx 10);
is %bag{Bla.new: :3a, :b<test>}, 10;

my value-class TweakMutator {
  has $.a = 42;

  method TWEAK(|) { $!a = 13 }

  method mutate-a($a) { $!a = $a }
}

my $a = TweakMutator.new;
is $a.a, 13;

throws-like {
  $a.mutate-a(3.14)
}, message => /"Cannot assign to an immutable value"/, X::AdHoc;

my $which := (my $b := TweakMutator.new).WHICH;
is $which, $b.WHICH;

$which does role :: { method unique-string { 999 } }

is $which.unique-string, 999;
is $b.WHICH.unique-string, $which.unique-string;

is $which, (my $c := TweakMutator.new).WHICH;
ok !$c.WHICH.^can: "unique-string";

my value-class Typed {
  has Int @.int-array;
  has Int %.int-hash;
}

is Typed.new(:int-array[1,2,3]).int-array, Tuple.new(1,2,3);
is Typed.new(:int-hash{:1a, :2b}).int-hash, ValueMap.new((:1a, :2b));

throws-like {
  Typed.new: :int-array[1, 2, "a"]
}, message => /"does not pass the type constraint (Positional[Int])"/, X::AdHoc;

throws-like {
  Typed.new: :int-hash{:1a, :2c, :c<a>}
}, message => /"does not pass the type constraint (Associative[Int])"/, X::AdHoc;

lives-ok {
  my value-class Bla { has $.a = 42 }
  is Bla.new.a, 42
}

eval-dies-ok 'my value-class Bla { has @.a = 1, 2, 3 }';
eval-dies-ok 'my value-class Bla { has %.a = a => 1 }';

lives-ok {
  my value-class Bla {
    has @.a;

    method TWEAK() {
      @!a := Tuple.new: 1, 2, 3 unless @!a.elems
    }
  }

  is Bla.new.a, Tuple.new: 1, 2, 3;
  is Bla.new(:a[10, 20, 30]).a, Tuple.new: 10, 20, 30;
}

lives-ok {
  my value-class Bla {
    has %.a;

    method TWEAK() {
      %!a := ValueMap.new: (a => 1, b => 2) unless %!a.elems
    }
  }

  is Bla.new.a, ValueMap.new: (a => 1, b => 2);
  is Bla.new(:a(a => 10, b => 20)).a, ValueMap.new: (a => 10, b => 20);
}

my value-class Clonable {
  has $.a;
  has $.b = 42;
  has @.c;
  has @.d;
  has %.e;
  has %.f;

  method TWEAK {
    @!d := Tuple.new: 1, 2, 3  unless @!d;
    %!f := ValueMap.new: (:1a, :2b) unless %!f;
  }
}
my $clonable = Clonable.new;
is $clonable.clone.gist, Clonable.new.gist;

is $clonable.clone(:1a, :c[10,20,30], :e{:10a, :20b}).gist, Clonable.new(:1a, :c[10,20,30], :e{:10a, :20b}).gist;

is $clonable.clone(:1b, :d[10,20,30], :f{:10a, :20b}).gist, Clonable.new(:1b, :d[10,20,30], :f{:10a, :20b}).gist;

is value-class Valid { has ::?CLASS $.a }.new.WHICH, 'Valid|a|Valid';

done-testing;


