Aion

 view release on metacpan or  search on metacpan

lib/Aion/Types.pm  view on Meta::CPAN


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...]", 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;
		sub External($) {
			local $_ = $_[0][0];
			UNIVERSAL::isa($_, 'Aion::Type')? $_:
			defined($_) && ref $_ eq ""? Object([$_]): do {
				CodeLike()->validate($_, "External type");
				Aion::Type->new(
					name => 'External',
					as => &Item,
					args => $_[0],
					test => $_,
					UNIVERSAL::can($_, 'coerce')
						? (coerce => [[&Any, (sub { my ($ex) = @_; sub { $ex->coerce } })->($_)]])
						: (),
				)
			}
		}
		subtype "Bool", as &Item, where { ref $_ eq "" and /^(1|0|)\z/ };
		subtype "BoolLike", as &Item, where {
			return 1 if overload::Method($_, 'bool');
			my $m = overload::Method($_, '0+');
			Bool()->include($m ? $m->($_) : $_) };
		subtype "Enum[e...]", 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 \$_ };
				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[start]", as &Str,
						init_where { M = qr/^${\ quotemeta A}/ },
						where { $_ =~ M };
					subtype "EndsWith[end]", 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\s+html|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[regexp]", as &Str, where { $_ =~ A };
					subtype "ClassName", as &Str, where { !!$_->can('new') };
					subtype "RoleName", as &Str, where { !$_->can('new') && !!(@{"$_\::ISA"} || first { *{$_}{CODE} } values %{"$_\::"}) };
					subtype "StrRat", as &Str, where { m!\s*/\s*!? &Num->include($`) && &Num->include($`): &Num->test };
					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 "Ref", as &Defined, where { "" ne ref $_ };
				subtype "Tied`[class]", 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 "NamedCode[subname]", as &CodeRef, where { Sub::Util::subname($_) ~~ A };
					subtype "ProtoCode[prototype]", as &CodeRef, where { Sub::Util::prototype($_) ~~ A };
					subtype "ForwardRef", as &CodeRef, where { !subref_is_reachable($_) };
					subtype "ImplementRef", as &CodeRef, where { subref_is_reachable($_) };
					subtype "Isa[type...]", as &CodeRef,
						init_where {
						    my $pkg = caller(2);
							SELF->{args} = [ map { External([UNIVERSAL::isa($_, 'Aion::Type')? $_: $pkg->can($_)? $pkg->can($_)->(): $_]) } ARGS ]
						}
						where {
							my $subroutine = $Aion::Isa{pack "J", refaddr $_} or return "";
							my $signature = $subroutine->{signature};
							my $args = ARGS;
							return "" if @$signature != @$args;
							my $i = 0;
							for my $type (@$args) {
								return "" unless $signature->[$i++] eq $type;
							}
							1
						};

lib/Aion/Types.pm  view on Meta::CPAN

		subtype SpeakOfKitty => as StrMatch[qr/\bkitty\b/i],
			message { "Speak is'nt included kitty!" };
	}
	
	"Kitty!" ~~ SpeakOfKitty # -> 1
	"abc"    ~~ SpeakOfKitty # -> ""
	
	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 exports routines:

=over

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

=item * C<SELF>, C<ARGS>, C<A>, C<B>, C<C>, C<D>, C<M>, C<N> - for use in validators of a type and its arguments.

=item * C<coerce>, C<from>, C<via> - to create a value converter from one class to another.

=back

Validator hierarchy:

	Any
		Control
			Union[A, B...]
			Intersection[A, B...]
			Exclude[A...]
			Option[A]
			Wantarray[A, B]
		Item
			External[type]
			Bool
			BoolLike
			Enum[e...]
			Maybe[A]
			Undef
			Defined
				Value
					Version
					Str
						Uni
						Bin
						NonEmptyStr
						StartsWith[start]
						EndsWith[end]
						Email
						Tel
						Url
						Path
						Html
						StrDate
						StrDateTime
						StrMatch[regexp]
						ClassName
						RoleName
						Join[separator]
						Split[separator]
						StrRat
						Num
							PositiveNum
							Int
								PositiveInt
								Nat
				Ref
					Tied`[class]
					LValueRef
					FormatRef
					CodeRef
						NamedCode[subname]
						ProtoCode[prototype]
						ForwardRef
						ImplementRef
						Isa[A...]
					RegexpRef
					ValueRef`[A]
						ScalarRef`[A]
						RefRef`[A]
					GlobRef
						FileHandle
					ArrayRef`[A]
					HashRef`[A]
					Object`[class]
						Me
						Rat
					Map[A => B]
					Tuple[A...]
					CycleTuple[A...]
					Dict[k => A, ...]
					RegexpLike
					CodeLike
					ArrayLike`[A]
						Lim[from, to?]
					HashLike`[A]
						HasProp[p...]
						LimKeys[from, to?]
				Like
					HasMethods[m...]
					Overload`[m...]
					InstanceOf[class...]
					ConsumerOf[role...]
					StrLike
						Len[from, to?]
					NumLike
						Float

lib/Aion/Types.pm  view on Meta::CPAN

	undef ~~ Defined # -> ""

=head2 Value

Defined values without references.

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

=head2 Len[from, to?]

Specifies a length value from C<from> to C<to>, or from 0 to C<from> if C<to> is missing.

	"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 version.

	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, including numbers.

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

=head2 Uni

Unicode strings with the utf8 flag or if decoding to utf8 occurs without errors.

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

=head2 Bin

Binary strings without the utf8 flag and octets with numbers less than 128.

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

=head2 StartsWith[begin]

The line starts with C<begin>.

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

=head2 EndsWith[end]

The line ends with C<end>.

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

=head2 NonEmptyStr

A string containing one or more non-blank characters.

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

=head2 Email

Lines with C<@>.

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

=head2 Tel

The telephone format is a plus sign and seven or more digits.

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

=head2 Url

Website URLs are a string prefixed with http:// or https://.

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

=head2 Path

Paths start with a slash.

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

=head2 Html

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

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

=head2 StrDate

Date in C<yyyy-mm-dd> format.

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

=head2 StrDateTime

Date and time in the 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[regexp]

Matches a string against a regular expression.

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

=head2 ClassName

The class name is a package with a C<new> method.

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

=head2 RoleName

The role name is a package without the C<new> method, with C<@ISA>, or with any one method.

	package ExRole1 {
		sub any_method {}
	}
	
	package ExRole2 {
		our @ISA = qw/ExRole1/;
	}
	
	
	'ExRole1' ~~ RoleName    # -> 1
	'ExRole2' ~~ RoleName    # -> 1
	'Aion::Type' ~~ RoleName # -> ""
	'Nouname::Empty::Package' ~~ RoleName # -> ""

=head2 StrRat

String representation of rational numbers.

Since in perl rational numbers are supported using the C<bigrat> pragma, which turns all rational numbers into C<Math::BigRat>, it is used in a ghost to C<Rat>.

	"6/7" ~~ StrRat  # -> 1
	"-6/7" ~~ StrRat # -> 1



( run in 1.353 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )