Group
Extension

Aion/lib/Aion/Types.pm

package Aion::Types;
# Типы-валидаторы для Aion

use 5.22.0;
no strict; no warnings; no diagnostics;
use common::sense;

use Aion::Type;
use Attribute::Handlers;
use List::Util qw/all any/;
use Exporter qw/import/;
use Scalar::Util qw/looks_like_number reftype blessed/;
use Sub::Util qw/prototype set_prototype subname set_subname/;

require Exporter;
our @EXPORT = our @EXPORT_OK = grep {
	*{$Aion::Types::{$_}}{CODE}	&& !/^(_|(NaN|import|all|any|looks_like_number|reftype|blessed|prototype|set_prototype|subname set_subname)\z)/n
} keys %Aion::Types::;

sub UNIVERSAL::Isa : ATTR(CODE) {
    my ($pkg, $symbol, $referent, $attr, $data, $phase, $file, $line) = @_;
    my $args_of_meth = "Arguments of method `" . *{$symbol}{NAME} . "`";
    my $returns_of_meth = "Returns of method `" . *{$symbol}{NAME} . "`";
    my $return_of_meth = "Return of method `" . *{$symbol}{NAME} . "`";

	my @signature = map { ref($_)? $_: $pkg->can($_)->() } @$data;

	my $ret = pop @signature;

    my ($ret_array, $ret_scalar) = exists $ret->{is_wantarray}? @{$ret->{args}}: (Tuple([$ret]), $ret);

    my $args = Tuple(\@signature);

    my $sub = sub {
        $args->validate(\@_, $args_of_meth);
        wantarray? do {
            my @returns = $referent->(@_);
            $ret_array->validate(\@returns, $returns_of_meth);
            @returns
        }: do {
            my $return = $referent->(@_);
            $ret_scalar->validate($return, $return_of_meth);
            $return
        }
    };

	set_prototype prototype($referent), $sub;
	set_subname subname($referent) . "__Isa", $sub;

	*$symbol = $sub
}

BEGIN {
my $TRUE = sub {1};

# Создание типа
sub subtype(@) {
	my $save = my $name = shift;
	my %o = @_;
	
	my ($as, $init_where, $where, $awhere, $message) = delete @o{qw/as init_where where awhere message/};

	die "subtype $save unused keys left: " . join ", ", keys %o if keys %o;

	my $is_maybe_arg; my $is_arg;
	$name =~ s/(`?)(\[.*)/ $is_maybe_arg = $1; $is_arg = $2; ''/e;

	my $pkg = scalar caller;
	die "subtype $save: ${pkg}::$name exists!" if *{"${pkg}::$name"}{CODE};

	if($is_maybe_arg) {
		die "subtype $save: needs a awhere" if !$awhere;
	} else {
		die "subtype $save: awhere is excess" if $awhere;
	}
	
	die "subtype $save: needs a where" if $is_arg && !($where || $awhere);

	if($as && $as->{test} != $TRUE) {
		if(!$where && !$awhere) {
			$where = (sub { my ($as) = @_; sub { $as->test } })->($as);
		} else {
			$where = (sub { my ($as, $where) = @_; sub { $as->test && $where->(@_) } })->($as, $where) if $where;
			$awhere = (sub { my ($as, $awhere) = @_; sub { $as->test && $awhere->(@_) } })->($as, $awhere) if $awhere;
		}
	}

	my $type = Aion::Type->new(name => $name);
	
	$type->{detail} = $message if $message;
	$type->{init} = $init_where if $init_where;
	$type->{as} = $as if $as;

	if($is_maybe_arg) {
		$type->{test} = $where;
		$type->{a_test} = $awhere;
		$type->make_maybe_arg($pkg)
	} elsif($is_arg) {
		$type->{test} = $where;
		$type->make_arg($pkg)
	} else {
		$type->{test} = $where // $TRUE;
		$type->make($pkg)
	}
}
}

sub as($) { (as => @_) }
sub init_where(&@) { (init_where => @_) }
sub where(&@) { (where => @_) }
sub awhere(&@) { (awhere => @_) }
sub message(&@) { (message => @_) }

sub SELF() { $Aion::Type::SELF }
sub ARGS() { wantarray? @{$Aion::Type::SELF->{args}}: $Aion::Type::SELF->{args} }
sub A() { $Aion::Type::SELF->{args}[0] }
sub B() { $Aion::Type::SELF->{args}[1] }
sub C() { $Aion::Type::SELF->{args}[2] }
sub D() { $Aion::Type::SELF->{args}[3] }

sub M() :lvalue { $Aion::Type::SELF->{M} }
sub N() :lvalue { $Aion::Type::SELF->{N} }

# Создание транслятора. У типа может быть сколько угодно трансляторов из других типов
# coerce Type, from OtherType, via {...}
sub coerce(@) {
	my ($type, %o) = @_;
	my ($from, $via) = delete @o{qw/from via/};

	die "coerce $type unused keys left: " . join ", ", keys %o if keys %o;
	die "coerce $type not Aion::Type!" unless UNIVERSAL::isa($type, "Aion::Type");
	die "coerce $type: from is'nt Aion::Type!" unless UNIVERSAL::isa($from, "Aion::Type");
	die "coerce $type: via is not subroutine!" unless ref $via eq "CODE";

	push @{$type->{coerce}}, [$from, $via];
	return;
}

sub from($) { (from => $_[0]) }
sub via(&) { (via => $_[0]) }

BEGIN {

subtype "Any";
	subtype "Control", as &Any;
		subtype "Union[A, B...]", as &Control,
			where { my $val = $_; any { $_->include($val) } ARGS };
		subtype "Intersection[A, B...]", as &Control,
			where { my $val = $_; all { $_->include($val) } ARGS };
		subtype "Exclude[A, B...]", as &Control,
			where { my $val = $_; !any { $_->include($val) } ARGS };
		subtype "Option[A]", as &Control,
			init_where {
				SELF->{is_option} = 1;
				Tuple([Object(["Aion::Type"])])->validate(scalar ARGS, "Arguments Option[A]")
			}
			where { A->test };
		subtype "Wantarray[A, S]", as &Control,
			init_where {
				SELF->{is_wantarray} = 1;
				Tuple([Object(["Aion::Type"]), Object(["Aion::Type"])])->validate(scalar ARGS, "Arguments Wantarray[A, S]")
			}
			where { ... };


	subtype "Item", as &Any;
		subtype "Bool", as &Item, where { ref $_ eq "" and /^(1|0|)\z/ };
		subtype "Enum[A...]", as &Item, where { $_ ~~ ARGS };
		subtype "Maybe[A]", as &Item, where { !defined($_) || A->test };
		subtype "Undef", as &Item, where { !defined $_ };
		subtype "Defined", as &Item, where { defined $_ };
			subtype "Value", as &Defined, where { "" eq ref $_ };
				subtype "Version", as &Value, where { "VSTRING" eq ref \$_ };
				my $init_limit = sub { if(@{&ARGS} == 1) { SELF->{min} = 0; SELF->{max} = A } else { SELF->{min} = A; SELF->{max} = B } };
				subtype "Str", as &Value, where { "SCALAR" eq ref \$_ };
					subtype "Uni", as &Str,	where { utf8::is_utf8($_) || /[\x80-\xFF]/a };
					subtype "Bin", as &Str, where { !utf8::is_utf8($_) && !/[\x80-\xFF]/a };
					subtype "NonEmptyStr", as &Str,	where { /\S/ };
					subtype "StartsWith[S]", as &Str,
						init_where { M = qr/^${\ quotemeta A}/ },
						where { $_ =~ M };
					subtype "EndsWith[S]", as &Str,
						init_where { N = qr/${\ quotemeta A}$/ },
						where { $_ =~ N };
					subtype "Email", as &Str, where { /@/ };
					subtype "Tel", as &Str, where { /^\+\d{7,}\z/ };
					subtype "Url", as &Str, where { /^https?:\/\// };
					subtype "Path", as &Str, where { /^\// };
					subtype "Html", as &Str, where { /^\s*<(!doctype|html)\b/i };
					subtype "StrDate", as &Str, where { /^\d{4}-\d{2}-\d{2}\z/ };
					subtype "StrDateTime", as &Str, where { /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\z/ };
					subtype "StrMatch[qr/.../]", as &Str, where { $_ =~ A };
					subtype "ClassName", as &Str, where { !!$_->can('new') };
					subtype "RoleName", as &Str, where { !!$_->can('requires') };

					subtype "Num", as &Str, where { looks_like_number($_) && /[\dfn]\z/i };
						subtype "PositiveNum", as &Num, where { $_ >= 0 };
						subtype "Int", as &Num,	where { /^-?\d+\z/ };
							subtype "PositiveInt", as &Int, where { $_ >= 0 };
							subtype "Nat", as &Int, where { $_ > 0 };
					subtype "Rat", as &Str, where { &Num->test || /^(-?\d+(\/\d+)?)\z/in };


			subtype "Ref", as &Defined, where { "" ne ref $_ };
				subtype "Tied`[A]", as &Ref,
					where { my $ref = reftype($_); !!(
						$ref eq "HASH"? tied %$_:
						$ref eq "ARRAY"? tied @$_:
						$ref eq "SCALAR"? tied $$_:
						0
					) }
					awhere { my $ref = reftype($_);
						$ref eq "HASH"? A eq ref tied %$_:
						$ref eq "ARRAY"? A eq ref tied @$_:
						$ref eq "SCALAR"? A eq ref tied $$_:
						""
					};
				subtype "LValueRef", as &Ref, where { ref $_ eq "LVALUE" };
				subtype "FormatRef", as &Ref, where { ref $_ eq "FORMAT" };
				subtype "CodeRef", as &Ref, where { ref $_ eq "CODE" };
				subtype "RegexpRef", as &Ref, where { ref $_ eq "Regexp" };
				subtype "ScalarRef`[A]", as &Ref,
					where { ref $_ eq "SCALAR" }
					awhere { ref $_ eq "SCALAR" && A->include($$_) };
				subtype "RefRef`[A]", as &Ref,
					where { ref $_ eq "REF" }
					awhere { ref $_ eq "REF" && A->include($$_) };
				subtype "GlobRef", as &Ref,
					where { ref $_ eq "GLOB" };
				subtype "ArrayRef`[A]", as &Ref,
					where { ref $_ eq "ARRAY" }
					awhere { my $A = A; ref $_ eq "ARRAY" && all { $A->test } @$_ };
				subtype "HashRef`[H]", as &Ref,
					where { ref $_ eq "HASH" }
					awhere { my $A = A; ref $_ eq "HASH" && all { $A->test } values %$_ };
				subtype "Object`[O]", as &Ref,
					where { blessed($_) ne "" }
					awhere { UNIVERSAL::isa($_, A) };
				subtype "Map[K, V]", as &HashRef,
					where {
						my ($K, $V) = ARGS;
						while(my ($k, $v) = each %$_) {
							return "" unless $K->include($k) && $V->include($v);
						}
						return 1;
					};

				my $tuple_args = ArrayRef([Object(['Aion::Type'])]);
				subtype "Tuple[A...]", as &ArrayRef,
					init_where { $tuple_args->validate(scalar ARGS, "Arguments Tuple[A...]") }
					where {
						my $k = 0;
						for my $A (ARGS) {
							return "" if $A->exclude($_->[$k++]);
						}
						$k == @$_
					};
				subtype "CycleTuple[A...]", as &ArrayRef,
					init_where { $tuple_args->validate(scalar ARGS, "Arguments CycleTuple[A...]") }
					where {
						my $k = 0;
						while($k < @$_) {
							for my $A (ARGS) {
								return "" if $A->exclude($_->[$k++]);
							}
						}
						$k == @$_
					};
				my $dict_args = CycleTuple([&Str, Object(['Aion::Type'])]);
				subtype "Dict[k => A, ...]", as &HashRef,
					init_where { $dict_args->validate(scalar ARGS, "Arguments Dict[k => A, ...]") }
					where {
						my $count = 0; my $k;
						for my $A (ARGS) {
							$k = $A, next unless ref $A;
							if(exists $_->{$k}) {
								return "" if $A->exclude($_->{$k});
								$count++;
							} else {
								return "" if !exists $A->{is_option};
							}
						}
						$count == keys %$_
					};
			subtype "RegexpLike", as &Ref,
				where { reftype($_) eq "REGEXP" || !!overload::Method($_, 'qr') };
			subtype "CodeLike", as &Ref,
				where { reftype($_) eq "CODE" || !!overload::Method($_, '&{}') };
			subtype "ArrayLike`[A]", as &Ref,
				where { reftype($_) eq "ARRAY" || !!overload::Method($_, '@{}') }
				awhere { &ArrayLike->test && do { my $A = A; all { $A->test } @$_ }};
				subtype "Lim[A, B?]", as &ArrayLike,
					init_where => $init_limit,
					where { SELF->{min} <= @$_ <= SELF->{max} };
			subtype "HashLike`[A]", as &Ref,
				where { reftype($_) eq "HASH" || !!overload::Method($_, "%{}") }
				awhere { &HashLike->test && do { my $A = A; all { $A->test } values %$_ }};
					subtype "HasProp[p...]", as &HashLike,
						where { my $x = $_; all { exists $x->{$_} } ARGS };
					subtype "LimKeys[A, B?]", as &HashLike,
						init_where => $init_limit,
						where { SELF->{min} <= scalar keys %$_ <= SELF->{max} };

			subtype "Like", as (&Str | &Object);
				subtype "HasMethods[m...]", as &Like,
					where { my $x = $_; all { $x->can($_) } ARGS };
				subtype "Overload`[m...]", as &Like,
					where { !!overload::Overloaded($_) }
					awhere { my $x = $_; all { overload::Method($x, $_) } ARGS };
				subtype "InstanceOf[A...]", as &Like, where { my $x = $_; all { $x->isa($_) } ARGS };
				subtype "ConsumerOf[A...]", as &Like, where { my $x = $_; all { $x->can("does") && $x->does($_) } ARGS };
				subtype "StrLike", as (&Str | Overload(['""']));
					subtype "Len[A, B?]", as &StrLike,
						init_where => $init_limit,
						where { SELF->{min} <= length($_) <= SELF->{max} };

				subtype "NumLike", where { looks_like_number($_) };
					subtype "Float", as &NumLike, where { -3.402823466E+38 <= $_ <= 3.402823466E+38 };
					subtype "Double", as &NumLike, where { -1.7976931348623158e+308 <= $_ <= 1.7976931348623158e+308 };
					subtype "Range[from, to]", as &NumLike, where { A <= $_ <= B };

					my $_8bits;
					subtype "Bytes[N]", as &NumLike,
						init_where {
							my $bits = A < 8? 8: ($_8bits //= do {
								require Math::BigInt;
								Math::BigInt->new(8)
							});
							my $N = 1 << ($bits * A - 1);
							N = -$N;
							M = $N-1;
						}
						where { N <= $_ <= M };
					subtype "PositiveBytes[N]", as &NumLike,
						init_where {
							my $bits = A < 8? 8: ($_8bits //= do {
								require Math::BigInt;
								Math::BigInt->new(8)
							});
							M = (1 << ($bits*A)) - 1;
						}
						where { 0 <= $_ <= M };

	coerce &Str => from &Undef => via { "" };
	coerce &Int => from &Num => via { int($_+($_ < 0? -.5: .5)) };
	coerce &Bool => from &Any => via { !!$_ };
};

1;

__END__

=encoding utf-8

=head1 NAME

Aion::Types is a library of validators. And it makes new validators

=head1 SYNOPSIS

	use Aion::Types;
	
	BEGIN {
	    subtype SpeakOfKitty => as StrMatch[qr/\bkitty\b/i],
	        message { "Speak is'nt included kitty!" };
	}
	
	"Kitty!" ~~ SpeakOfKitty # -> 1
	"abc" ~~ SpeakOfKitty 	 # -> ""
	
	eval { SpeakOfKitty->validate("abc", "This") }; "$@" # ~> Speak is'nt included kitty!
	
	
	BEGIN {
		subtype IntOrArrayRef => as (Int | ArrayRef);
	}
	
	[] ~~ IntOrArrayRef  # -> 1
	35 ~~ IntOrArrayRef  # -> 1
	"" ~~ IntOrArrayRef  # -> ""
	
	
	coerce IntOrArrayRef, from Num, via { int($_ + .5) };
	
	IntOrArrayRef->coerce(5.5) # => 6

=head1 DESCRIPTION

This module export subroutines:

=over

=item * C<subtype>, C<as>, C<init_where>, C<where>, C<awhere>, C<message> — for create validators.

=item * C<SELF>, C<ARGS>, C<A>, C<B>, C<C>, C<D>, C<M>, C<N> — for use in validators has arguments.

=item * C<coerce>, C<from>, C<via> — for create coerce, using for translate values from one class to other class.

=back

Hierarhy of validators:

	Any
		Control
			Union[A, B...]
			Intersection[A, B...]
			Exclude[A, B...]
			Option[A]
			Wantarray[A, S]
		Item
			Bool
			Enum[A...]
			Maybe[A]
			Undef
			Defined
				Value
					Version
					Str
						Uni
						Bin
						NonEmptyStr
						StartsWith
						EndsWith
						Email
						Tel
						Url
						Path
						Html
						StrDate
						StrDateTime
						StrMatch[qr/.../]
						ClassName[A]
						RoleName[A]
						Rat
						Num
							PositiveNum
							Int
								PositiveInt
								Nat
				Ref
					Tied`[A]
					LValueRef
					FormatRef
					CodeRef
					RegexpRef
					ScalarRef`[A]
					RefRef`[A]
					GlobRef`[A]
					ArrayRef`[A]
					HashRef`[H]
					Object`[O]
					Map[K, V]
					Tuple[A...]
					CycleTuple[A...]
					Dict[k => A, ...]
					RegexpLike
					CodeLike
					ArrayLike`[A]
						Lim[A, B?]
					HashLike`[A]
						HasProp[p...]
						LimKeys[A, B?]
				Like
					HasMethods[m...]
					Overload`[m...]
					InstanceOf[A...]
					ConsumerOf[A...]
					StrLike
						Len[A, B?]
					NumLike
						Float
						Double
						Range[from, to]
						Bytes[A, B?]
						PositiveBytes[A, B?]
	

=head1 SUBROUTINES

=head2 subtype ($name, @paraphernalia)

Make new type.

	BEGIN {
		subtype One => where { $_ == 1 } message { "Actual 1 only!" };
	}
	
	1 ~~ One 	# -> 1
	0 ~~ One 	# -> ""
	eval { One->validate(0) }; $@ # ~> Actual 1 only!

C<where> and C<message> is syntax sugar, and C<subtype> can be used without them.

	BEGIN {
		subtype Many => (where => sub { $_ > 1 });
	}
	
	2 ~~ Many  # -> 1
	
	eval { subtype Many => (where1 => sub { $_ > 1 }) }; $@ # ~> subtype Many unused keys left: where1
	
	eval { subtype 'Many' }; $@ # ~> subtype Many: main::Many exists!

=head2 as ($parenttype)

Use with C<subtype> for extended create type of C<$parenttype>.

=head2 init_where ($code)

Initialize type with new arguments. Use with C<subtype>.

	BEGIN {
		subtype 'LessThen[A]',
			init_where { Num->validate(A, "Argument LessThen[A]") }
			where { $_ < A };
	}
	
	eval { LessThen["string"] }; $@  # ~> Argument LessThen\[A\]
	
	5 ~~ LessThen[5]  # -> ""

=head2 where ($code)

Set in type C<$code> as test. Value for test set in C<$_>.

	BEGIN {
		subtype 'Two',
			where { $_ == 2 };
	}
	
	2 ~~ Two # -> 1
	3 ~~ Two # -> ""

Use with C<subtype>. Need if is the required arguments.

	eval { subtype 'Ex[A]' }; $@  # ~> subtype Ex\[A\]: needs a where

=head2 awhere ($code)

Use with C<subtype>. 

If type maybe with and without arguments, then use for set test with arguments, and C<where> - without.

	BEGIN {
		subtype 'GreatThen`[A]',
			where { $_ > 0 }
			awhere { $_ > A }
		;
	}
	
	0 ~~ GreatThen    # -> ""
	1 ~~ GreatThen    # -> 1
	
	3 ~~ GreatThen[3] # -> ""
	4 ~~ GreatThen[3] # -> 1

Need if arguments is optional.

	eval { subtype 'Ex`[A]', where {} }; $@  # ~> subtype Ex`\[A\]: needs a awhere
	eval { subtype 'Ex', awhere {} }; $@  # ~> subtype Ex: awhere is excess
	
	BEGIN {
		subtype 'MyEnum`[A...]',
			as Str,
			awhere { $_ ~~ scalar ARGS }
		;
	}
	
	"ab" ~~ MyEnum[qw/ab cd/] # -> 1

=head2 SELF

The current type. C<SELF> use in C<init_where>, C<where> and C<awhere>.

=head2 ARGS

Arguments of the current type. In scalar context returns array ref on the its. And in array context returns its. Use in C<init_where>, C<where> and C<awhere>.

=head2 A, B, C, D

First, second, third and fifth argument of the type.

	BEGIN {
		subtype "Seria[A,B,C,D]", where { A < B < $_ < C < D };
	}
	
	2.5 ~~ Seria[1,2,3,4]   # -> 1

Use in C<init_where>, C<where> and C<awhere>.

=head2 M, N

C<M> and C<N> is the reduction for C<< SELF-E<gt>{M} >> and C<< SELF-E<gt>{N} >>.

	BEGIN {
		subtype "BeginAndEnd[A, B]",
			init_where {
				N = qr/^${\ quotemeta A}/;
				M = qr/${\ quotemeta B}$/;
			}
			where { $_ =~ N && $_ =~ M };
	}
	
	"Hi, my dear!" ~~ BeginAndEnd["Hi,", "!"]   # -> 1
	"Hi my dear!" ~~ BeginAndEnd["Hi,", "!"]   # -> ""
	
	BeginAndEnd["Hi,", "!"]   # => BeginAndEnd['Hi,', '!']

=head2 message ($code)

Use with C<subtype> for make the message on error, if the value excluded the type. In C<$code> use subroutine: C<SELF> - the current type, C<ARGS>, C<A>, C<B>, C<C>, C<D> - arguments of type (if is), and the testing value in C<$_>. It can be stringified using C<< SELF-E<gt>val_to_str($_) >>.

=head2 coerce ($type, from => $from, via => $via)

It add new coerce ($via) to C<$type> from C<$from>-type.

	BEGIN {subtype Four => where {4 eq $_}}
	
	"4a" ~~ Four	# -> ""
	
	Four->coerce("4a")	# -> "4a"
	
	coerce Four, from Str, via { 0+$_ };
	
	Four->coerce("4a")	# -> 4
	
	coerce Four, from ArrayRef, via { scalar @$_ };
	
	Four->coerce([1,2,3])	# -> 3
	Four->coerce([1,2,3]) ~~ Four	# -> ""
	Four->coerce([1,2,3,4]) ~~ Four	# -> 1

C<coerce> throws exeptions:

	eval {coerce Int, via1 => 1}; $@  # ~> coerce Int unused keys left: via1
	eval {coerce "x"}; $@  # ~> coerce x not Aion::Type!
	eval {coerce Int}; $@  # ~> coerce Int: from is'nt Aion::Type!
	eval {coerce Int, from "x"}; $@  # ~> coerce Int: from is'nt Aion::Type!
	eval {coerce Int, from Num}; $@  # ~> coerce Int: via is not subroutine!
	eval {coerce Int, (from=>Num, via=>"x")}; $@  # ~> coerce Int: via is not subroutine!

Standart coerces:

	# Str from Undef — empty string
	Str->coerce(undef) # -> ""
	
	# Int from Num — rounded integer
	Int->coerce(2.5) # -> 3
	Int->coerce(-2.5) # -> -3
	
	# Bool from Any — 1 or ""
	Bool->coerce([])	# -> 1
	Bool->coerce(0)		# -> ""

=head2 from ($type)

Syntax sugar for C<coerce>.

=head2 via ($code)

Syntax sugar for C<coerce>.

=head1 ATTRIBUTES

=head2 Isa (@signature)

Check the subroutine signature: arguments and returns.

	sub minint($$) : Isa(Int => Int => Int) {
		my ($x, $y) = @_;
		$x < $y? $x : $y
	}
	
	minint 6, 5; # -> 5
	eval {minint 5.5, 2}; $@ # ~> Arguments of method `minint` must have the type Tuple\[Int, Int\]\.

Attribute C<Isa> is subroutine C<UNIVERSAL::Isa>.

	sub half($) {
		my ($x) = @_;
		$x / 2
	}
	
	UNIVERSAL::Isa(
		__PACKAGE__,
		*half,
		\&half,
		undef,
		[Int => Int],
	);
	
	half 4; # -> 2
	eval {half 5}; $@ # ~> Return of method `half` must have the type Int. The it is 2.5

=head1 TYPES

=head2 Any

Top-level type in the hierarchy. Match all.

=head2 Control

Top-level type in the hierarchy constructors new types from any types.

=head2 Union[A, B...]

Union many types. It analog operator C<$type1 | $type2>.

	33  ~~ Union[Int, Ref]    # -> 1
	[]  ~~ Union[Int, Ref]    # -> 1
	"a" ~~ Union[Int, Ref]    # -> ""

=head2 Intersection[A, B...]

Intersection many types. It analog operator C<$type1 & $type2>.

	15 ~~ Intersection[Int, StrMatch[/5/]]    # -> 1

=head2 Exclude[A, B...]

Exclude many types. It analog operator C<~ $type>.

	-5  ~~ Exclude[PositiveInt]    # -> 1
	"a" ~~ Exclude[PositiveInt]    # -> 1
	5   ~~ Exclude[PositiveInt]    # -> ""
	5.5 ~~ Exclude[PositiveInt]    # -> 1

If C<Exclude> has many arguments, then this analog C<~ ($type1 | $type2 ...)>.

	-5  ~~ Exclude[PositiveInt, Enum[-2]]    # -> 1
	-2  ~~ Exclude[PositiveInt, Enum[-2]]    # -> ""
	0   ~~ Exclude[PositiveInt, Enum[-2]]    # -> ""

=head2 Option[A]

The optional keys in the C<Dict>.

	{a=>55} ~~ Dict[a=>Int, b => Option[Int]] # -> 1
	{a=>55, b=>31} ~~ Dict[a=>Int, b => Option[Int]] # -> 1
	{a=>55, b=>31.5} ~~ Dict[a=>Int, b => Option[Int]] # -> ""

=head2 Wantarray[A, S]

if the subroutine returns different values in the context of an array and a scalar, then using type C<Wantarray> with type C<A> for array context and type C<S> for scalar context.

	sub arr : Isa(PositiveInt => Wantarray[ArrayRef[PositiveInt], PositiveInt]) {
		my ($n) = @_;
		wantarray? 1 .. $n: $n
	}
	
	my @a = arr(3);
	my $s = arr(3);
	
	\@a  # --> [1,2,3]
	$s	 # -> 3

=head2 Item

Top-level type in the hierarchy scalar types.

=head2 Bool

C<1> is true. C<0>, C<""> or C<undef> is false.

	1 ~~ Bool     # -> 1
	0 ~~ Bool     # -> 1
	undef ~~ Bool # -> 1
	"" ~~ Bool    # -> 1
	
	2 ~~ Bool     # -> ""
	[] ~~ Bool    # -> ""

=head2 Enum[A...]

Enumerate values.

	3 ~~ Enum[1,2,3]        	# -> 1
	"cat" ~~ Enum["cat", "dog"] # -> 1
	4 ~~ Enum[1,2,3]        	# -> ""

=head2 Maybe[A]

C<undef> or type in C<[]>.

	undef ~~ Maybe[Int]    # -> 1
	4 ~~ Maybe[Int]        # -> 1
	"" ~~ Maybe[Int]       # -> ""

=head2 Undef

C<undef> only.

	undef ~~ Undef    # -> 1
	0 ~~ Undef        # -> ""

=head2 Defined

All exclude C<undef>.

	\0 ~~ Defined       # -> 1
	undef ~~ Defined    # -> ""

=head2 Value

Defined unreference values.

	3 ~~ Value        # -> 1
	\3 ~~ Value       # -> ""
	undef ~~ Value    # -> ""

=head2 Len[A, B?]

Defines the length value from C<A> to C<B>, or from 0 to C<A> if C<B> is'nt present.

	"1234" ~~ Len[3]   # -> ""
	"123" ~~ Len[3]    # -> 1
	"12" ~~ Len[3]     # -> 1
	"" ~~ Len[1, 2]    # -> ""
	"1" ~~ Len[1, 2]   # -> 1
	"12" ~~ Len[1, 2]  # -> 1
	"123" ~~ Len[1, 2] # -> ""

=head2 Version

Perl versions.

	1.1.0 ~~ Version    # -> 1
	v1.1.0 ~~ Version   # -> 1
	v1.1 ~~ Version     # -> 1
	v1 ~~ Version       # -> 1
	1.1 ~~ Version      # -> ""
	"1.1.0" ~~ Version  # -> ""

=head2 Str

Strings, include numbers.

	1.1 ~~ Str         # -> 1
	"" ~~ Str          # -> 1
	1.1.0 ~~ Str       # -> ""

=head2 Uni

Unicode strings: with utf8-flag or decode to utf8 without error.

	"↭" ~~ Uni    # -> 1
	123 ~~ Uni    # -> ""
	do {no utf8; "↭" ~~ Uni}    # -> 1

=head2 Bin

Binary strings: without utf8-flag and octets with numbers less then 128.

	123 ~~ Bin    # -> 1
	"z" ~~ Bin    # -> 1
	"↭" ~~ Bin    # -> ""
	do {no utf8; "↭" ~~ Bin }   # -> ""

=head2 StartsWith[S]

The string starts with C<S>.

	"Hi, world!" ~~ StartsWith["Hi,"]	# -> 1
	"Hi world!" ~~ StartsWith["Hi,"]	# -> ""

=head2 EndsWith[S]

The string ends with C<S>.

	"Hi, world!" ~~ EndsWith["world!"]	# -> 1
	"Hi, world" ~~ EndsWith["world!"]	# -> ""

=head2 NonEmptyStr

String with one or many non-space characters.

	" " ~~ NonEmptyStr        # -> ""
	" S " ~~ NonEmptyStr      # -> 1
	" S " ~~ (NonEmptyStr & Len[2])   # -> ""

=head2 Email

Strings with C<@>.

	'@' ~~ Email      # -> 1
	'a@a.a' ~~ Email  # -> 1
	'a.a' ~~ Email    # -> ""

=head2 Tel

Format phones is plus sign and seven or great digits.

	"+1234567" ~~ Tel    # -> 1
	"+1234568" ~~ Tel    # -> 1
	"+ 1234567" ~~ Tel    # -> ""
	"+1234567 " ~~ Tel    # -> ""

=head2 Url

Web urls is string with prefix http:// or https://.

	"http://" ~~ Url    # -> 1
	"http:/" ~~ Url    # -> ""

=head2 Path

The paths starts with a slash.

	"/" ~~ Path     # -> 1
	"/a/b" ~~ Path  # -> 1
	"a/b" ~~ Path   # -> ""

=head2 Html

The html starts with a C<< E<lt>!doctype >> or C<< E<lt>html >>.

	"<HTML" ~~ Html            # -> 1
	" <html" ~~ Html           # -> 1
	" <!doctype html>" ~~ Html # -> 1
	" <html1>" ~~ Html         # -> ""

=head2 StrDate

The date is format C<yyyy-mm-dd>.

	"2001-01-12" ~~ StrDate    # -> 1
	"01-01-01" ~~ StrDate    # -> ""

=head2 StrDateTime

The dateTime is format C<yyyy-mm-dd HH:MM:SS>.

	"2012-12-01 00:00:00" ~~ StrDateTime     # -> 1
	"2012-12-01 00:00:00 " ~~ StrDateTime    # -> ""

=head2 StrMatch[qr/.../]

Match value with regular expression.

	' abc ' ~~ StrMatch[qr/abc/]    # -> 1
	' abbc ' ~~ StrMatch[qr/abc/]   # -> ""

=head2 ClassName

Classname is the package with method C<new>.

	'Aion::Type' ~~ ClassName     # -> 1
	'Aion::Types' ~~ ClassName    # -> ""

=head2 RoleName

Rolename is the package with subroutine C<requires>.

	package ExRole {
		sub requires {}
	}
	
	'ExRole' ~~ RoleName    	# -> 1
	'Aion::Type' ~~ RoleName    # -> ""

=head2 Rat

Rational numbers.

	"6/7" ~~ Rat     # -> 1
	"-6/7" ~~ Rat    # -> 1
	6 ~~ Rat         # -> 1
	"inf" ~~ Rat     # -> 1
	"+Inf" ~~ Rat    # -> 1
	"NaN" ~~ Rat     # -> 1
	"-nan" ~~ Rat    # -> 1
	6.5 ~~ Rat       # -> 1
	"6.5 " ~~ Rat    # -> ''

=head2 Num

The numbers.

	-6.5 ~~ Num      # -> 1
	6.5e-7 ~~ Num    # -> 1
	"6.5 " ~~ Num    # -> ""

=head2 PositiveNum

The positive numbers.

	0 ~~ PositiveNum     # -> 1
	0.1 ~~ PositiveNum   # -> 1
	-0.1 ~~ PositiveNum  # -> ""
	-0 ~~ PositiveNum    # -> 1

=head2 Float

The machine float number is 4 bytes.

	-4.8 ~~ Float    				# -> 1
	-3.402823466E+38 ~~ Float    	# -> 1
	+3.402823466E+38 ~~ Float    	# -> 1
	-3.402823467E+38 ~~ Float       # -> ""

=head2 Double

The machine float number is 8 bytes.

	-4.8 ~~ Double    					# -> 1
	-1.7976931348623158e+308 ~~ Double  # -> 1
	+1.7976931348623158e+308 ~~ Double  # -> 1
	-1.7976931348623159e+308 ~~ Double # -> ""

=head2 Range[from, to]

Numbers between C<from> and C<to>.

	1 ~~ Range[1, 3]    # -> 1
	2.5 ~~ Range[1, 3]  # -> 1
	3 ~~ Range[1, 3]    # -> 1
	3.1 ~~ Range[1, 3]  # -> ""
	0.9 ~~ Range[1, 3]  # -> ""

=head2 Int

Integers.

	123 ~~ Int    # -> 1
	-12 ~~ Int    # -> 1
	5.5 ~~ Int    # -> ""

=head2 Bytes[N]

C<N> - the number of bytes for limit.

	-129 ~~ Bytes[1]    # -> ""
	-128 ~~ Bytes[1]    # -> 1
	127 ~~ Bytes[1]     # -> 1
	128 ~~ Bytes[1]     # -> ""
	
	# 2 bits power of (8 bits * 8 bytes - 1)
	my $N = 1 << (8*8-1);
	(-$N-1) ~~ Bytes[8]   # -> ""
	(-$N) ~~ Bytes[8]     # -> 1
	($N-1) ~~ Bytes[8]  	# -> 1
	$N ~~ Bytes[8]      	# -> ""
	
	require Math::BigInt;
	
	my $N17 = 1 << (8*Math::BigInt->new(17) - 1);
	
	((-$N17-1) . "") ~~ Bytes[17]  # -> ""
	(-$N17 . "") ~~ Bytes[17]  # -> 1
	(($N17-1) . "") ~~ Bytes[17]  # -> 1
	($N17 . "") ~~ Bytes[17]  # -> ""

=head2 PositiveInt

Positive integers.

	+0 ~~ PositiveInt    # -> 1
	-0 ~~ PositiveInt    # -> 1
	55 ~~ PositiveInt    # -> 1
	-1 ~~ PositiveInt    # -> ""

=head2 PositiveBytes[N]

C<N> - the number of bytes for limit.

	-1 ~~ PositiveBytes[1]    # -> ""
	0 ~~ PositiveBytes[1]    # -> 1
	255 ~~ PositiveBytes[1]    # -> 1
	256 ~~ PositiveBytes[1]    # -> ""
	
	-1 ~~ PositiveBytes[8] # -> ""
	1.01 ~~ PositiveBytes[8] # -> ""
	0 ~~ PositiveBytes[8] # -> 1
	
	my $N8 = 2 ** (8*Math::BigInt->new(8)) - 1;
	
	$N8 . "" ~~ PositiveBytes[8] # -> 1
	($N8+1) . "" ~~ PositiveBytes[8] # -> ""
	
	-1 ~~ PositiveBytes[17] # -> ""
	0 ~~ PositiveBytes[17] # -> 1

=head2 Nat

Integers 1+.

	0 ~~ Nat    # -> ""
	1 ~~ Nat    # -> 1

=head2 Ref

The value is reference.

	\1 ~~ Ref    # -> 1
	1 ~~ Ref     # -> ""

=head2 Tied`[A]

The reference on the tied variable.

	package TiedHash { sub TIEHASH { bless {@_}, shift } }
	package TiedArray { sub TIEARRAY { bless {@_}, shift } }
	package TiedScalar { sub TIESCALAR { bless {@_}, shift } }
	
	tie my %a, "TiedHash";
	tie my @a, "TiedArray";
	tie my $a, "TiedScalar";
	my %b; my @b; my $b;
	
	\%a ~~ Tied    # -> 1
	\@a ~~ Tied    # -> 1
	\$a ~~ Tied    # -> 1
	
	\%b ~~ Tied    # -> ""
	\@b ~~ Tied    # -> ""
	\$b ~~ Tied    # -> ""
	\\$b ~~ Tied    # -> ""
	
	ref tied %a  # => TiedHash
	ref tied %{\%a}  # => TiedHash
	
	\%a ~~ Tied["TiedHash"]     # -> 1
	\@a ~~ Tied["TiedArray"]    # -> 1
	\$a ~~ Tied["TiedScalar"]   # -> 1
	
	\%a ~~ Tied["TiedArray"]    # -> ""
	\@a ~~ Tied["TiedScalar"]   # -> ""
	\$a ~~ Tied["TiedHash"]     # -> ""
	\\$a ~~ Tied["TiedScalar"]     # -> ""
	
	

=head2 LValueRef

The function allows assignment.

	ref \substr("abc", 1, 2) # => LVALUE
	ref \vec(42, 1, 2) # => LVALUE
	
	\substr("abc", 1, 2) ~~ LValueRef # -> 1
	\vec(42, 1, 2) ~~ LValueRef # -> 1

But it with C<: lvalue> do'nt working.

	sub abc: lvalue { $_ }
	
	abc() = 12;
	$_ # => 12
	ref \abc()  # => SCALAR
	\abc() ~~ LValueRef	# -> ""
	
	
	package As {
		sub x : lvalue {
			shift->{x};
		}
	}
	
	my $x = bless {}, "As";
	$x->x = 10;
	
	$x->x # => 10
	$x    # --> bless {x=>10}, "As"
	
	ref \$x->x 			# => SCALAR
	\$x->x ~~ LValueRef # -> ""

And on the end:

	\1 ~~ LValueRef	# -> ""
	
	my $x = "abc";
	substr($x, 1, 1) = 10;
	
	$x # => a10c
	
	LValueRef->include(\substr($x, 1, 1))	# => 1

=head2 FormatRef

The format.

	format EXAMPLE_FMT =
	@<<<<<<   @||||||   @>>>>>>
	"left",   "middle", "right"
	.
	
	*EXAMPLE_FMT{FORMAT} ~~ FormatRef   # -> 1
	\1 ~~ FormatRef    			# -> ""

=head2 CodeRef

Subroutine.

	sub {} ~~ CodeRef    # -> 1
	\1 ~~ CodeRef        # -> ""

=head2 RegexpRef

The regular expression.

	qr// ~~ RegexpRef    # -> 1
	\1 ~~ RegexpRef    	 # -> ""

=head2 ScalarRef`[A]

The scalar.

	\12 ~~ ScalarRef     		# -> 1
	\\12 ~~ ScalarRef    		# -> ""
	\-1.2 ~~ ScalarRef[Num]     # -> 1
	\\-1.2 ~~ ScalarRef[Num]     # -> ""

=head2 RefRef`[A]

The ref as ref.

	\\1 ~~ RefRef    # -> 1
	\1 ~~ RefRef     # -> ""
	\\1.3 ~~ RefRef[ScalarRef[Num]]    # -> 1
	\1.3 ~~ RefRef[ScalarRef[Num]]    # -> ""

=head2 GlobRef

The global.

	\*A::a ~~ GlobRef    # -> 1
	*A::a ~~ GlobRef     # -> ""

=head2 ArrayRef`[A]

The arrays.

	[] ~~ ArrayRef    # -> 1
	{} ~~ ArrayRef    # -> ""
	[] ~~ ArrayRef[Num]    # -> 1
	{} ~~ ArrayRef[Num]    # -> ''
	[1, 1.1] ~~ ArrayRef[Num]    # -> 1
	[1, undef] ~~ ArrayRef[Num]    # -> ""

=head2 Lim[A, B?]

Limit arrays from C<A> to C<B>, or from 0 to C<A>, if C<B> is'nt present.

	[] ~~ Lim[5] # -> 1
	[1..5] ~~ Lim[5] # -> 1
	[1..6] ~~ Lim[5] # -> ""
	
	[1..5] ~~ Lim[1,5] # -> 1
	[1..6] ~~ Lim[1,5] # -> ""
	
	[1] ~~ Lim[1,5] # -> 1
	[] ~~ Lim[1,5] # -> ""

=head2 HashRef`[H]

The hashes.

	{} ~~ HashRef    # -> 1
	\1 ~~ HashRef    # -> ""
	
	[]  ~~ HashRef[Int]    # -> ""
	{x=>1, y=>2}  ~~ HashRef[Int]    # -> 1
	{x=>1, y=>""} ~~ HashRef[Int]    # -> ""

=head2 Object`[O]

The blessed values.

	bless(\(my $val=10), "A1") ~~ Object    # -> 1
	\(my $val=10) ~~ Object			    	# -> ""
	
	bless(\(my $val=10), "A1") ~~ Object["A1"]   # -> 1
	bless(\(my $val=10), "A1") ~~ Object["B1"]   # -> ""

=head2 Map[K, V]

As C<HashRef>, but has type for keys also.

	{} ~~ Map[Int, Int]    		 # -> 1
	{5 => 3} ~~ Map[Int, Int]    # -> 1
	+{5.5 => 3} ~~ Map[Int, Int] # -> ""
	{5 => 3.3} ~~ Map[Int, Int]  # -> ""
	{5 => 3, 6 => 7} ~~ Map[Int, Int]  # -> 1

=head2 Tuple[A...]

The tuple.

	["a", 12] ~~ Tuple[Str, Int]    # -> 1
	["a", 12, 1] ~~ Tuple[Str, Int]    # -> ""
	["a", 12.1] ~~ Tuple[Str, Int]    # -> ""

=head2 CycleTuple[A...]

The tuple one or more times.

	["a", -5] ~~ CycleTuple[Str, Int]    # -> 1
	["a", -5, "x"] ~~ CycleTuple[Str, Int]    # -> ""
	["a", -5, "x", -6] ~~ CycleTuple[Str, Int]    # -> 1
	["a", -5, "x", -6.2] ~~ CycleTuple[Str, Int]    # -> ""

=head2 Dict[k => A, ...]

The dictionary.

	{a => -1.6, b => "abc"} ~~ Dict[a => Num, b => Str]    # -> 1
	
	{a => -1.6, b => "abc", c => 3} ~~ Dict[a => Num, b => Str]    # -> ""
	{a => -1.6} ~~ Dict[a => Num, b => Str]    # -> ""
	
	{a => -1.6} ~~ Dict[a => Num, b => Option[Str]]    # -> 1

=head2 HasProp[p...]

The hash has the properties.

	[0, 1] ~~ HasProp[qw/0 1/]	# -> ""
	
	{a => 1, b => 2, c => 3} ~~ HasProp[qw/a b/]    # -> 1
	{a => 1, b => 2} ~~ HasProp[qw/a b/]    # -> 1
	{a => 1, c => 3} ~~ HasProp[qw/a b/]    # -> ""
	
	bless({a => 1, b => 3}, "A") ~~ HasProp[qw/a b/]    # -> 1

=head2 Like

The object or string.

	"" ~~ Like    	# -> 1
	1 ~~ Like    	# -> 1
	bless({}, "A") ~~ Like    # -> 1
	bless([], "A") ~~ Like    # -> 1
	bless(\(my $str = ""), "A") ~~ Like    # -> 1
	\1 ~~ Like    	# -> ""

=head2 HasMethods[m...]

The object or the class has the methods.

	package HasMethodsExample {
		sub x1 {}
		sub x2 {}
	}
	
	"HasMethodsExample" ~~ HasMethods[qw/x1 x2/]    		# -> 1
	bless({}, "HasMethodsExample") ~~ HasMethods[qw/x1 x2/] # -> 1
	bless({}, "HasMethodsExample") ~~ HasMethods[qw/x1/]    # -> 1
	"HasMethodsExample" ~~ HasMethods[qw/x3/]    			# -> ""
	"HasMethodsExample" ~~ HasMethods[qw/x1 x2 x3/]    		# -> ""
	"HasMethodsExample" ~~ HasMethods[qw/x1 x3/]    		# -> ""

=head2 Overload`[op...]

The object or the class is overloaded.

	package OverloadExample {
		use overload '""' => sub { "abc" };
	}
	
	"OverloadExample" ~~ Overload    # -> 1
	bless({}, "OverloadExample") ~~ Overload    # -> 1
	"A" ~~ Overload    				# -> ""
	bless({}, "A") ~~ Overload    	# -> ""

And it has the operators if arguments are specified.

	"OverloadExample" ~~ Overload['""']   # -> 1
	"OverloadExample" ~~ Overload['|']    # -> ""

=head2 InstanceOf[A...]

The class or the object inherits the list of classes.

	package Animal {}
	package Cat { our @ISA = qw/Animal/ }
	package Tiger { our @ISA = qw/Cat/ }
	
	
	"Tiger" ~~ InstanceOf['Animal', 'Cat']  # -> 1
	"Tiger" ~~ InstanceOf['Tiger']    		# -> 1
	"Tiger" ~~ InstanceOf['Cat', 'Dog']    	# -> ""

=head2 ConsumerOf[A...]

The class or the object has the roles.

The presence of the role is checked by the C<does> method.

	package NoneExample {}
	package RoleExample { sub does { $_[1] ~~ [qw/Role1 Role2/] } }
	
	'RoleExample' ~~ ConsumerOf[qw/Role1/] # -> 1
	'RoleExample' ~~ ConsumerOf[qw/Role2 Role1/] # -> 1
	bless({}, 'RoleExample') ~~ ConsumerOf[qw/Role3 Role2 Role1/] # -> ""
	
	'NoneExample' ~~ ConsumerOf[qw/Role1/]	# -> ""

=head2 StrLike

String or object with overloaded operator C<"">.

	"" ~~ StrLike    							# -> 1
	
	package StrLikeExample {
		use overload '""' => sub { "abc" };
	}
	
	bless({}, "StrLikeExample") ~~ StrLike    	# -> 1
	
	{} ~~ StrLike    							# -> ""

=head2 RegexpLike

The regular expression or the object with overloaded operator C<qr>.

	ref(qr//)  # => Regexp
	Scalar::Util::reftype(qr//)  # => REGEXP
	
	my $regex = bless qr//, "A";
	Scalar::Util::reftype($regex) # => REGEXP
	
	$regex ~~ RegexpLike    # -> 1
	qr// ~~ RegexpLike    	# -> 1
	"" ~~ RegexpLike    	# -> ""
	
	package RegexpLikeExample {
		use overload 'qr' => sub { qr/abc/ };
	}
	
	"RegexpLikeExample" ~~ RegexpLike    # -> ""
	bless({}, "RegexpLikeExample") ~~ RegexpLike    # -> 1

=head2 CodeLike

The subroutines.

	sub {} ~~ CodeLike    	# -> 1
	\&CodeLike ~~ CodeLike  # -> 1
	{} ~~ CodeLike  		# -> ""

=head2 ArrayLike`[A]

The arrays or objects with  or overloaded operator C<@{}>.

	{} ~~ ArrayLike    		# -> ""
	{} ~~ ArrayLike[Int]    # -> ""
	
	[] ~~ ArrayLike    	# -> 1
	
	package ArrayLikeExample {
		use overload '@{}' => sub {
			shift->{array} //= []
		};
	}
	
	my $x = bless {}, 'ArrayLikeExample';
	$x->[1] = 12;
	$x->{array}  # --> [undef, 12]
	
	$x ~~ ArrayLike    # -> 1
	
	$x ~~ ArrayLike[Int]    # -> ""
	
	$x->[0] = 13;
	$x ~~ ArrayLike[Int]    # -> 1

=head2 HashLike`[A]

The hashes or objects with overloaded operator C<%{}>.

	{} ~~ HashLike    	# -> 1
	[] ~~ HashLike    	# -> ""
	[] ~~ HashLike[Int] # -> ""
	
	package HashLikeExample {
		use overload '%{}' => sub {
			shift->[0] //= {}
		};
	}
	
	my $x = bless [], 'HashLikeExample';
	$x->{key} = 12.3;
	$x->[0]  # --> {key => 12.3}
	
	$x ~~ HashLike    	   # -> 1
	$x ~~ HashLike[Int]    # -> ""
	$x ~~ HashLike[Num]    # -> 1

=head1 AUTHOR

Yaroslav O. Kosmina LL<mailto:dart@cpan.org>

=head1 LICENSE

⚖ B<GPLv3>

=head1 COPYRIGHT

The Aion::Types module is copyright © 2023 Yaroslav O. Kosmina. Rusland. All rights reserved.


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.