Initial Commit

This commit is contained in:
Riley Schneider
2025-12-03 16:38:10 +01:00
parent c5e26bf594
commit b732d8d4b5
17680 changed files with 5977495 additions and 2 deletions

View File

@@ -0,0 +1,600 @@
package Specio::Library::Builtins;
use strict;
use warnings;
our $VERSION = '0.46';
use parent 'Specio::Exporter';
use List::Util 1.33 ();
use overload ();
use re ();
use Scalar::Util ();
use Specio::Constraint::Parameterizable;
use Specio::Declare;
use Specio::Helpers ();
BEGIN {
local $@ = undef;
my $has_ref_util
= eval { require Ref::Util; Ref::Util->VERSION('0.112'); 1 };
sub _HAS_REF_UTIL () {$has_ref_util}
}
declare(
'Item',
inline => sub {'1'}
);
declare(
'Undef',
parent => t('Item'),
inline => sub {
'!defined(' . $_[1] . ')';
}
);
declare(
'Defined',
parent => t('Item'),
inline => sub {
'defined(' . $_[1] . ')';
}
);
declare(
'Bool',
parent => t('Item'),
inline => sub {
return sprintf( <<'EOF', ( $_[1] ) x 7 );
(
(
!ref( %s )
&& (
!defined( %s )
|| %s eq q{}
|| %s eq '1'
|| %s eq '0'
)
)
||
(
Scalar::Util::blessed( %s )
&& defined overload::Method( %s, 'bool' )
)
)
EOF
}
);
declare(
'Value',
parent => t('Defined'),
inline => sub {
$_[0]->parent->inline_check( $_[1] ) . ' && !ref(' . $_[1] . ')';
}
);
declare(
'Ref',
parent => t('Defined'),
# no need to call parent - ref also checks for definedness
inline => sub { 'ref(' . $_[1] . ')' }
);
declare(
'Str',
parent => t('Value'),
inline => sub {
return sprintf( <<'EOF', ( $_[1] ) x 6 );
(
(
defined( %s )
&& !ref( %s )
&& (
( ref( \%s ) eq 'SCALAR' )
|| do { ( ref( \( my $val = %s ) ) eq 'SCALAR' ) }
)
)
||
(
Scalar::Util::blessed( %s )
&& defined overload::Method( %s, q{""} )
)
)
EOF
}
);
my $value_type = t('Value');
declare(
'Num',
parent => t('Str'),
inline => sub {
return sprintf( <<'EOF', ( $_[1] ) x 5 );
(
(
defined( %s )
&& !ref( %s )
&& (
do {
( my $val = %s ) =~
/\A
-?[0-9]+(?:\.[0-9]+)?
(?:[Ee][\-+]?[0-9]+)?
\z/x
}
)
)
||
(
Scalar::Util::blessed( %s )
&& defined overload::Method( %s, '0+' )
)
)
EOF
}
);
declare(
'Int',
parent => t('Num'),
inline => sub {
return sprintf( <<'EOF', ( $_[1] ) x 6 );
(
(
defined( %s )
&& !ref( %s )
&& (
do { ( my $val1 = %s ) =~ /\A-?[0-9]+(?:[Ee]\+?[0-9]+)?\z/ }
)
)
||
(
Scalar::Util::blessed( %s )
&& defined overload::Method( %s, '0+' )
&& do { ( my $val2 = %s + 0 ) =~ /\A-?[0-9]+(?:[Ee]\+?[0-9]+)?\z/ }
)
)
EOF
}
);
{
my $ref_check
= _HAS_REF_UTIL
? 'Ref::Util::is_plain_coderef(%s)'
: q{ref(%s) eq 'CODE'};
declare(
'CodeRef',
parent => t('Ref'),
inline => sub {
return sprintf( <<"EOF", ( $_[1] ) x 3 );
(
$ref_check
||
(
Scalar::Util::blessed( %s )
&& defined overload::Method( %s, '&{}' )
)
)
EOF
}
);
}
{
# This is a 5.8 back-compat shim stolen from Type::Tiny's Devel::Perl58Compat
# module.
unless ( exists &re::is_regexp || _HAS_REF_UTIL ) {
require B;
*re::is_regexp = sub {
## no critic (ErrorHandling::RequireCheckingReturnValueOfEval)
eval { B::svref_2object( $_[0] )->MAGIC->TYPE eq 'r' };
};
}
my $ref_check
= _HAS_REF_UTIL
? 'Ref::Util::is_regexpref(%s)'
: 're::is_regexp(%s)';
declare(
'RegexpRef',
parent => t('Ref'),
inline => sub {
return sprintf( <<"EOF", ( $_[1] ) x 3 );
(
$ref_check
||
(
Scalar::Util::blessed( %s )
&& defined overload::Method( %s, 'qr' )
)
)
EOF
},
);
}
{
my $ref_check
= _HAS_REF_UTIL
? 'Ref::Util::is_plain_globref(%s)'
: q{ref( %s ) eq 'GLOB'};
declare(
'GlobRef',
parent => t('Ref'),
inline => sub {
return sprintf( <<"EOF", ( $_[1] ) x 3 );
(
$ref_check
||
(
Scalar::Util::blessed( %s )
&& defined overload::Method( %s, '*{}' )
)
)
EOF
}
);
}
{
my $ref_check
= _HAS_REF_UTIL
? 'Ref::Util::is_plain_globref(%s)'
: q{ref( %s ) eq 'GLOB'};
# NOTE: scalar filehandles are GLOB refs, but a GLOB ref is not always a
# filehandle
declare(
'FileHandle',
parent => t('Ref'),
inline => sub {
return sprintf( <<"EOF", ( $_[1] ) x 6 );
(
(
$ref_check
&& Scalar::Util::openhandle( %s )
)
||
(
Scalar::Util::blessed( %s )
&&
(
%s->isa('IO::Handle')
||
(
defined overload::Method( %s, '*{}' )
&& Scalar::Util::openhandle( *{ %s } )
)
)
)
)
EOF
}
);
}
{
my $ref_check
= _HAS_REF_UTIL
? 'Ref::Util::is_blessed_ref(%s)'
: 'Scalar::Util::blessed(%s)';
declare(
'Object',
parent => t('Ref'),
inline => sub { sprintf( $ref_check, $_[1] ) },
);
}
declare(
'ClassName',
parent => t('Str'),
inline => sub {
return
sprintf(
<<'EOF', $_[0]->parent->inline_check( $_[1] ), ( $_[1] ) x 2 );
(
( %s )
&& length "%s"
&& Specio::Helpers::is_class_loaded( "%s" )
)
EOF
},
);
{
my $ref_check
= _HAS_REF_UTIL
? 'Ref::Util::is_plain_scalarref(%s) || Ref::Util::is_plain_refref(%s)'
: q{ref( %s ) eq 'SCALAR' || ref( %s ) eq 'REF'};
my $base_scalarref_check = sub {
return sprintf( <<"EOF", ( $_[0] ) x 4 );
(
(
$ref_check
)
||
(
Scalar::Util::blessed( %s )
&& defined overload::Method( %s, '\${}' )
)
)
EOF
};
declare(
'ScalarRef',
type_class => 'Specio::Constraint::Parameterizable',
parent => t('Ref'),
inline => sub { $base_scalarref_check->( $_[1] ) },
parameterized_inline_generator => sub {
my $self = shift;
my $parameter = shift;
my $val = shift;
return sprintf(
'( ( %s ) && ( %s ) )',
$base_scalarref_check->($val),
$parameter->inline_check( '${' . $val . '}' ),
);
}
);
}
{
my $ref_check
= _HAS_REF_UTIL
? 'Ref::Util::is_plain_arrayref(%s)'
: q{ref( %s ) eq 'ARRAY'};
my $base_arrayref_check = sub {
return sprintf( <<"EOF", ( $_[0] ) x 3 );
(
$ref_check
||
(
Scalar::Util::blessed( %s )
&& defined overload::Method( %s, '\@{}' )
)
)
EOF
};
declare(
'ArrayRef',
type_class => 'Specio::Constraint::Parameterizable',
parent => t('Ref'),
inline => sub { $base_arrayref_check->( $_[1] ) },
parameterized_inline_generator => sub {
my $self = shift;
my $parameter = shift;
my $val = shift;
return sprintf(
'( ( %s ) && ( List::Util::all { %s } @{ %s } ) )',
$base_arrayref_check->($val),
$parameter->inline_check('$_'),
$val,
);
}
);
}
{
my $ref_check
= _HAS_REF_UTIL
? 'Ref::Util::is_plain_hashref(%s)'
: q{ref( %s ) eq 'HASH'};
my $base_hashref_check = sub {
return sprintf( <<"EOF", ( $_[0] ) x 3 );
(
$ref_check
||
(
Scalar::Util::blessed( %s )
&& defined overload::Method( %s, '%%{}' )
)
)
EOF
};
declare(
'HashRef',
type_class => 'Specio::Constraint::Parameterizable',
parent => t('Ref'),
inline => sub { $base_hashref_check->( $_[1] ) },
parameterized_inline_generator => sub {
my $self = shift;
my $parameter = shift;
my $val = shift;
return sprintf(
'( ( %s ) && ( List::Util::all { %s } values %%{ %s } ) )',
$base_hashref_check->($val),
$parameter->inline_check('$_'),
$val,
);
}
);
}
declare(
'Maybe',
type_class => 'Specio::Constraint::Parameterizable',
parent => t('Item'),
inline => sub {'1'},
parameterized_inline_generator => sub {
my $self = shift;
my $parameter = shift;
my $val = shift;
return sprintf( <<'EOF', $val, $parameter->inline_check($val) );
( !defined( %s ) || ( %s ) )
EOF
},
);
1;
# ABSTRACT: Implements type constraint objects for Perl's built-in types
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Library::Builtins - Implements type constraint objects for Perl's built-in types
=head1 VERSION
version 0.46
=head1 DESCRIPTION
This library provides a set of types parallel to those provided by Moose.
The types are in the following hierarchy
Item
Bool
Maybe (of `a)
Undef
Defined
Value
Str
Num
Int
ClassName
Ref
ScalarRef (of `a)
ArrayRef (of `a)
HashRef (of `a)
CodeRef
RegexpRef
GlobRef
FileHandle
Object
=head2 Item
Accepts any value
=head2 Bool
Accepts a non-reference that is C<undef>, an empty string, C<0>, or C<1>. It
also accepts any object which overloads boolification.
=head2 Maybe (of `a)
A parameterizable type which accepts C<undef> or the type C<`a>. If not
parameterized this type will accept any value.
=head2 Undef
Only accepts C<undef>.
=head2 Value
Accepts any non-reference value.
=head2 Str
Accepts any non-reference value or an object which overloads stringification.
=head2 Num
Accepts nearly the same values as C<Scalar::Util::looks_like_number>, but does
not accept numbers with leading or trailing spaces, infinities, or NaN. Also
accepts an object which overloads numification.
=head2 Int
Accepts any integer value, or an object which overloads numification and
numifies to an integer.
=head2 ClassName
Accepts any value which passes C<Str> where the string is a loaded package.
=head2 Ref
Accepts any reference.
=head2 ScalarRef (of `a)
Accepts a scalar reference or an object which overloads scalar
dereferencing. If parameterized, the dereferenced value must be of type C<`a>.
=head2 ArrayRef (of `a)
Accepts a array reference or an object which overloads array dereferencing. If
parameterized, the values in the arrayref must be of type C<`a>.
=head2 HashRef (of `a)
Accepts a hash reference or an object which overloads hash dereferencing. If
parameterized, the values in the hashref must be of type C<`a>.
=head2 CodeRef
Accepts a code (sub) reference or an object which overloads code
dereferencing.
=head2 RegexpRef
Accepts a regex object created by C<qr//> or an object which overloads
regex interpolation.
=head2 GlobRef
Accepts a glob reference or an object which overloads glob dereferencing.
=head2 FileHandle
Accepts a glob reference which is an open file handle, any C<IO::Handle>
Object or subclass, or an object which overloads glob dereferencing and
returns a glob reference which is an open file handle.
=head2 Object
Accepts any blessed object.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,218 @@
package Specio::Library::Numeric;
use strict;
use warnings;
our $VERSION = '0.46';
use parent 'Specio::Exporter';
use Specio::Declare;
use Specio::Library::Builtins;
declare(
'PositiveNum',
parent => t('Num'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
%s > 0
)
EOF
},
);
declare(
'PositiveOrZeroNum',
parent => t('Num'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
%s >= 0
)
EOF
},
);
declare(
'PositiveInt',
parent => t('Int'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
%s > 0
)
EOF
},
);
declare(
'PositiveOrZeroInt',
parent => t('Int'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
%s >= 0
)
EOF
},
);
declare(
'NegativeNum',
parent => t('Num'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
%s < 0
)
EOF
},
);
declare(
'NegativeOrZeroNum',
parent => t('Num'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
%s <= 0
)
EOF
},
);
declare(
'NegativeInt',
parent => t('Int'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
%s < 0
)
EOF
},
);
declare(
'NegativeOrZeroInt',
parent => t('Int'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
%s <= 0
)
EOF
},
);
declare(
'SingleDigit',
parent => t('Int'),
inline => sub {
return
sprintf(
<<'EOF', $_[0]->parent->inline_check( $_[1] ), ( $_[1] ) x 2 );
(
%s
&&
%s >= -9
&&
%s <= 9
)
EOF
},
);
1;
# ABSTRACT: Implements type constraint objects for some common numeric types
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Library::Numeric - Implements type constraint objects for some common numeric types
=head1 VERSION
version 0.46
=head1 DESCRIPTION
This library provides some additional string numeric for common cases.
=head2 PositiveNum
=head2 PositiveOrZeroNum
=head2 PositiveInt
=head2 PositiveOrZeroInt
=head2 NegativeNum
=head2 NegativeOrZeroNum
=head2 NegativeInt
=head2 NegativeOrZeroInt
=head2 SingleDigit
A single digit from -9 to 9.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,208 @@
package Specio::Library::Perl;
use strict;
use warnings;
our $VERSION = '0.46';
use parent 'Specio::Exporter';
use Specio::Library::String;
use version 0.83 ();
use Specio::Declare;
my $package_inline = sub {
return sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
%s =~ /\A[^\W\d]\w*(?:::\w+)*\z/
)
EOF
};
declare(
'PackageName',
parent => t('NonEmptyStr'),
inline => $package_inline,
);
declare(
'ModuleName',
parent => t('NonEmptyStr'),
inline => $package_inline,
);
declare(
'DistName',
parent => t('NonEmptyStr'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
%s =~ /\A[^\W\d]\w*(?:-\w+)*\z/
)
EOF
},
);
declare(
'Identifier',
parent => t('NonEmptyStr'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
%s =~ /\A[^\W\d]\w*\z/
)
EOF
},
);
declare(
'SafeIdentifier',
parent => t('Identifier'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
%s !~ /\A[_ab]\z/
)
EOF
},
);
declare(
'LaxVersionStr',
parent => t('NonEmptyStr'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
version::is_lax(%s)
)
EOF
},
);
declare(
'StrictVersionStr',
parent => t('NonEmptyStr'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
version::is_strict(%s)
)
EOF
},
);
1;
# ABSTRACT: Implements type constraint objects for some common Perl language things
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Library::Perl - Implements type constraint objects for some common Perl language things
=head1 VERSION
version 0.46
=head1 DESCRIPTION
This library provides some additional string types for common cases.
=head2 PackageName
A valid package name. Unlike the C<ClassName> constraint from the
L<Specio::Library::Builtins> library, this package does not need to be loaded.
This type does allow Unicode characters.
=head2 ModuleName
Same as C<PackageName>.
=head2 DistName
A valid distribution name like C<DBD-Pg> Basically this is the same as a
package name with the double-colons replaced by dashes. Note that there are
some historical distribution names that don't fit this pattern, like
C<CGI.pm>.
This type does allow Unicode characters.
=head2 Identifier
An L<Identifier|perldata/Variable names> is something that could be used as a
symbol name or other identifier (filehandle, directory handle, subroutine
name, format name, or label). It's what you put after the sigil (dollar sign,
at sign, percent sign) in a variable name. Generally, it's a bunch of
word characters not starting with a digit.
This type does allow Unicode characters.
=head2 SafeIdentifier
This is just like an C<Identifier> but it excludes the single-character
variables underscore (C<_>), C<a>< and C<b>, as these are special variables to
the Perl interpreter.
=head2 LaxVersionStr and StrictVersionStr
Lax and strict version strings use the L<is_lax|version/is_lax> and
L<is_strict|version/is_strict> methods from C<version> to check if the given
string would be a valid lax or strict version. L<version::Internals> covers
the details but basically: lax versions are everything you may do, and strict
omit many of the usages best avoided.
=head2 CREDITS
Much of the code and docs for this library comes from MooseX::Types::Perl,
written by Ricardo SIGNES <rjbs@cpan.org>.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,127 @@
package Specio::Library::String;
use strict;
use warnings;
our $VERSION = '0.46';
use parent 'Specio::Exporter';
use Specio::Declare;
use Specio::Library::Builtins;
declare(
'NonEmptySimpleStr',
parent => t('Str'),
inline => sub {
return
sprintf(
<<'EOF', $_[0]->parent->inline_check( $_[1] ), ( $_[1] ) x 3 );
(
%s
&&
length %s > 0
&&
length %s <= 255
&&
%s !~ /[\n\r\x{2028}\x{2029}]/
)
EOF
},
);
declare(
'NonEmptyStr',
parent => t('Str'),
inline => sub {
return
sprintf( <<'EOF', $_[0]->parent->inline_check( $_[1] ), $_[1] );
(
%s
&&
length %s
)
EOF
},
);
declare(
'SimpleStr',
parent => t('Str'),
inline => sub {
return
sprintf(
<<'EOF', $_[0]->parent->inline_check( $_[1] ), ( $_[1] ) x 2 );
(
%s
&&
length %s <= 255
&&
%s !~ /[\n\r\x{2028}\x{2029}]/
)
EOF
},
);
1;
# ABSTRACT: Implements type constraint objects for some common string types
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Library::String - Implements type constraint objects for some common string types
=head1 VERSION
version 0.46
=head1 DESCRIPTION
This library provides some additional string types for common cases.
=head2 NonEmptyStr
A string which has at least one character.
=head2 SimpleStr
A string that is 255 characters or less with no vertical whitespace
characters.
=head2 NonEmptySimpleStr
A non-empty string that is 255 characters or less with no vertical whitespace
characters.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,250 @@
package Specio::Library::Structured;
use strict;
use warnings;
our $VERSION = '0.46';
use parent 'Specio::Exporter';
use Carp qw( confess );
use List::Util ();
use Scalar::Util qw( blessed );
use Specio::Constraint::Structurable;
use Specio::Declare;
use Specio::Library::Builtins;
use Specio::Library::Structured::Dict;
use Specio::Library::Structured::Map;
use Specio::Library::Structured::Tuple;
use Specio::TypeChecks qw( does_role );
## no critic (Variables::ProtectPrivateVars)
declare(
'Dict',
type_class => 'Specio::Constraint::Structurable',
parent => Specio::Library::Structured::Dict->parent,
inline => \&Specio::Library::Structured::Dict::_inline,
parameterization_args_builder =>
\&Specio::Library::Structured::Dict::_parameterization_args_builder,
name_builder => \&Specio::Library::Structured::Dict::_name_builder,
structured_inline_generator =>
\&Specio::Library::Structured::Dict::_structured_inline_generator,
);
declare(
'Map',
type_class => 'Specio::Constraint::Structurable',
parent => Specio::Library::Structured::Map->parent,
inline => \&Specio::Library::Structured::Map::_inline,
parameterization_args_builder =>
\&Specio::Library::Structured::Map::_parameterization_args_builder,
name_builder => \&Specio::Library::Structured::Map::_name_builder,
structured_inline_generator =>
\&Specio::Library::Structured::Map::_structured_inline_generator,
);
declare(
'Tuple',
type_class => 'Specio::Constraint::Structurable',
parent => Specio::Library::Structured::Tuple->parent,
inline => \&Specio::Library::Structured::Tuple::_inline,
parameterization_args_builder =>
\&Specio::Library::Structured::Tuple::_parameterization_args_builder,
name_builder => \&Specio::Library::Structured::Tuple::_name_builder,
structured_inline_generator =>
\&Specio::Library::Structured::Tuple::_structured_inline_generator,
);
## use critic
sub optional {
return { optional => shift };
}
sub slurpy {
return { slurpy => shift };
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _also_export {qw( optional slurpy )}
## use critic
1;
# ABSTRACT: Structured types for Specio (Dict, Map, Tuple)
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Library::Structured - Structured types for Specio (Dict, Map, Tuple)
=head1 VERSION
version 0.46
=head1 SYNOPSIS
use Specio::Library::Builtins;
use Specio::Library::String;
use Specio::Library::Structured;
my $map = t(
'Map',
of => {
key => t('NonEmptyStr'),
value => t('Int'),
},
);
my $tuple = t(
'Tuple',
of => [ t('Str'), t('Num') ],
);
my $dict = t(
'Dict',
of => {
kv => {
name => t('Str'),
age => t('Int'),
},
},
);
=head1 DESCRIPTION
B<This particular library should be considered in an alpha state. The syntax
for defining structured types may change, as well as some of the internals of
its implementation.>
This library provides a set of structured types for Specio, C<Dict>, C<Map>,
and C<Tuple>. This library also exports two helper subs used for some types,
C<optional> and C<slurpy>.
All structured types are parameterized by calling C<< t( 'Type Name', of =>
... ) >>. The arguments passed after C<of> vary for each type.
=head2 Dict
A C<Dict> is a hashref with a well-defined set of keys and types for those
key.
The argument passed to C<of> should be a single hashref. That hashref must
contain a C<kv> key defining the expected keys and the types for their
values. This C<kv> value is itself a hashref. If a key/value pair is optional,
use C<optional> around the I<type> for that key:
my $person = t(
'Dict',
of => {
kv => {
first => t('NonEmptyStr'),
middle => optional( t('NonEmptyStr') ),
last => t('NonEmptyStr'),
},
},
);
If a key is optional, then it can be omitted entirely, but if it passed then
it's type will be checked, so it cannot just be set to C<undef>.
You can also pass a C<slurpy> key. If this is passed, then the C<Dict> will
allow other, unknown keys, as long as they match the specified type:
my $person = t(
'Dict',
of => {
kv => {
first => t('NonEmptyStr'),
middle => optional( t('NonEmptyStr') ),
last => t('NonEmptyStr'),
},
slurpy => t('Int'),
},
);
=head2 Map
A C<Map> is a hashref with specified types for its keys and values, but no
well-defined key names.
The argument passed to C<of> should be a single hashref with two keys, C<key>
and C<value>. The type for the C<key> will typically be some sort of key, but
if you're using a tied hash or an object with hash overloading it could
conceivably be any sort of value.
=head2 Tuple
A C<Tuple> is an arrayref with a fixed set of members in a specific order.
The argument passed to C<of> should be a single arrayref consisting of
types. You can mark a slot in the C<Tuple> as optional by wrapping the type in
a call to C<optional>:
my $record = t(
'Tuple',
of => [
t('PositiveInt'),
t('Str'),
optional( t('Num') ),
optional( t('Num') ),
],
);
You can have as many C<optional> elements as you want, but they must always
come in sequence at the end of the tuple definition. You cannot interleave
required and optional elements.
You can also make the Tuple accept an arbitrary number of values by wrapping
the last type in a call to C<slurpy>:
my $record = t(
'Tuple',
of => [
t('PositiveInt'),
t('Str'),
slurpy( t('Num') ),
],
);
In this case, the C<Tuple> will require the first two elements and then allow
any number (including zero) of C<Num> elements.
You cannot mix C<optional> and C<slurpy> in a C<Tuple> definition.
=for Pod::Coverage optional slurpy
=head1 LIMITATIONS
Currently all structured types require that the types they are structured with
can be inlined. This may change in the future, but inlining all your types is
a really good idea, so you should do that anyway.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,168 @@
package Specio::Library::Structured::Dict;
use strict;
use warnings;
our $VERSION = '0.46';
use Carp qw( confess );
use List::Util ();
use Scalar::Util qw( blessed );
use Specio::Helpers qw( perlstring );
use Specio::Library::Builtins;
use Specio::TypeChecks qw( does_role );
my $hashref = t('HashRef');
sub parent {$hashref}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _inline {
$hashref->inline_check( $_[1] );
}
sub _parameterization_args_builder {
my $self = shift;
my $args = shift;
for my $p ( ( $args->{slurpy} || () ), values %{ $args->{kv} } ) {
my $type;
if ( blessed($p) ) {
$type = $p;
}
else {
if ( ref $p eq 'HASH' && $p->{optional} ) {
$type = $p->{optional};
}
else {
confess
'Can only pass types, optional types, and slurpy types when defining a Dict';
}
}
does_role( $type, 'Specio::Constraint::Role::Interface' )
or confess
'All parameters passed to ->parameterize must be objects which do the Specio::Constraint::Role::Interface role';
confess
'All parameters passed to ->parameterize must be inlinable constraints'
unless $type->can_be_inlined;
}
return %{$args};
}
sub _name_builder {
my $self = shift;
my $p = shift;
## no critic (Subroutines::ProtectPrivateSubs)
my @kv;
for my $k ( sort keys %{ $p->{kv} } ) {
my $v = $p->{kv}{$k};
if ( blessed($v) ) {
push @kv, "$k => " . $self->_name_or_anon($v);
}
elsif ( $v->{optional} ) {
push @kv,
"$k => " . $self->_name_or_anon( $v->{optional} ) . '?';
}
}
if ( $p->{slurpy} ) {
push @kv, $self->_name_or_anon( $p->{slurpy} ) . '...';
}
return 'Dict{ ' . ( join ', ', @kv ) . ' }';
}
sub _structured_inline_generator {
my $self = shift;
my $val = shift;
my %args = @_;
my @code = sprintf( '( %s )', $hashref->_inline_check($val) );
for my $k ( sort keys %{ $args{kv} } ) {
my $p = $args{kv}{$k};
my $access = sprintf( '%s->{%s}', $val, perlstring($k) );
if ( !blessed($p) ) {
my $type = $p->{optional};
push @code,
sprintf(
'( exists %s ? ( %s ) : 1 )',
$access, $type->_inline_check($access)
);
}
else {
push @code, sprintf( '( %s )', $p->_inline_check($access) );
}
}
if ( $args{slurpy} ) {
my $check
= '( do { my %%_____known_____ = map { $_ => 1 } ( %s ); List::Util::all { %s } grep { ! $_____known_____{$_} } sort keys %%{ %s } } )';
push @code,
sprintf(
$check,
( join ', ', map { perlstring($_) } keys %{ $args{kv} } ),
$args{slurpy}->_inline_check( sprintf( '%s->{$_}', $val ) ),
$val,
);
}
return '( ' . ( join ' && ', @code ) . ' )';
}
1;
# ABSTRACT: Guts of Dict structured type
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Library::Structured::Dict - Guts of Dict structured type
=head1 VERSION
version 0.46
=head1 DESCRIPTION
There are no user facing parts here.
=for Pod::Coverage .*
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,124 @@
package Specio::Library::Structured::Map;
use strict;
use warnings;
our $VERSION = '0.46';
use Carp qw( confess );
use List::Util ();
use Specio::Library::Builtins;
use Specio::TypeChecks qw( does_role );
my $hashref = t('HashRef');
sub parent {$hashref}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _inline {
$hashref->inline_check( $_[1] );
}
sub _parameterization_args_builder {
my $self = shift;
my $args = shift;
for my $k (qw( key value )) {
does_role(
$args->{$k},
'Specio::Constraint::Role::Interface'
)
or confess
qq{The "$k" parameter passed to ->parameterize must be one or more objects which do the Specio::Constraint::Role::Interface role};
confess
qq{The "$k" parameter passed to ->parameterize must be an inlinable constraint}
unless $args->{$k}->can_be_inlined;
}
return map { $_ => $args->{$_} } qw( key value );
}
sub _name_builder {
my $self = shift;
my $p = shift;
## no critic (Subroutines::ProtectPrivateSubs)
return
'Map{ '
. $self->_name_or_anon( $p->{key} ) . ' => '
. $self->_name_or_anon( $p->{value} ) . ' }';
}
sub _structured_inline_generator {
my $self = shift;
my $val = shift;
my %args = @_;
my $code = <<'EOF';
(
( %s )
&& ( List::Util::all { %s } keys %%{ %s } )
&& ( List::Util::all { %s } values %%{ %s } )
)
EOF
return sprintf(
$code,
$hashref->_inline_check($val),
$args{key}->inline_check('$_'),
$val,
$args{value}->inline_check('$_'),
$val,
);
}
1;
# ABSTRACT: Guts of Map structured type
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Library::Structured::Map - Guts of Map structured type
=head1 VERSION
version 0.46
=head1 DESCRIPTION
There are no user facing parts here.
=for Pod::Coverage .*
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,218 @@
package Specio::Library::Structured::Tuple;
use strict;
use warnings;
our $VERSION = '0.46';
use Carp qw( confess );
use List::Util ();
use Scalar::Util qw( blessed );
use Specio::Library::Builtins;
use Specio::TypeChecks qw( does_role );
my $arrayref = t('ArrayRef');
sub parent {$arrayref}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _inline {
$arrayref->inline_check( $_[1] );
}
sub _parameterization_args_builder {
my $self = shift;
my $args = shift;
my $saw_slurpy;
my $saw_optional;
for my $p ( @{$args} ) {
if ($saw_slurpy) {
confess
'A Tuple cannot have any parameters after a slurpy parameter';
}
if ( $saw_optional && blessed($p) ) {
confess
'A Tuple cannot have a non-optional parameter after an optional parameter';
}
my $type;
if ( blessed($p) ) {
$type = $p;
}
else {
if ( ref $p eq 'HASH' ) {
if ( $p->{optional} ) {
$saw_optional = 1;
$type = $p->{optional};
}
if ( $p->{slurpy} ) {
$saw_slurpy = 1;
$type = $p->{slurpy};
}
}
else {
confess
'Can only pass types, optional types, and slurpy types when defining a Tuple';
}
}
if ( $saw_optional && $saw_slurpy ) {
confess
'Cannot defined a slurpy Tuple with optional slots as well';
}
does_role( $type, 'Specio::Constraint::Role::Interface' )
or confess
'All parameters passed to ->parameterize must be objects which do the Specio::Constraint::Role::Interface role';
confess
'All parameters passed to ->parameterize must be inlinable constraints'
unless $type->can_be_inlined;
}
return ( of => $args );
}
sub _name_builder {
my $self = shift;
my $p = shift;
my @names;
for my $m ( @{ $p->{of} } ) {
## no critic (Subroutines::ProtectPrivateSubs)
if ( blessed($m) ) {
push @names, $self->_name_or_anon($m);
}
elsif ( $m->{optional} ) {
push @names, $self->_name_or_anon( $m->{optional} ) . '?';
}
elsif ( $m->{slurpy} ) {
push @names, $self->_name_or_anon( $m->{slurpy} ) . '...';
}
}
return 'Tuple[ ' . ( join ', ', @names ) . ' ]';
}
sub _structured_inline_generator {
my $self = shift;
my $val = shift;
my %args = @_;
my @of = @{ $args{of} };
my $slurpy;
$slurpy = ( pop @of )->{slurpy}
if !blessed( $of[-1] ) && $of[-1]->{slurpy};
my @code = sprintf( '( %s )', $arrayref->_inline_check($val) );
unless ($slurpy) {
my $min = 0;
my $max = 0;
for my $p (@of) {
# Unblessed values are optional.
if ( blessed($p) ) {
$min++;
$max++;
}
else {
$max++;
}
}
if ($min) {
push @code,
sprintf(
'( @{ %s } >= %d && @{ %s } <= %d )',
$val, $min, $val, $max
);
}
}
for my $i ( 0 .. $#of ) {
my $p = $of[$i];
my $access = sprintf( '%s->[%d]', $val, $i );
if ( !blessed($p) ) {
my $type = $p->{optional};
push @code,
sprintf(
'( @{%s} >= %d ? ( %s ) : 1 )', $val, $i + 1,
$type->_inline_check($access)
);
}
else {
push @code,
sprintf( '( %s )', $p->_inline_check($access) );
}
}
if ($slurpy) {
my $non_slurpy = scalar @of;
my $check
= '( @{%s} > %d ? ( List::Util::all { %s } @{%s}[%d .. $#{%s}] ) : 1 )';
push @code,
sprintf(
$check,
$val, $non_slurpy, $slurpy->_inline_check('$_'),
$val, $non_slurpy, $val,
);
}
return '( ' . ( join ' && ', @code ) . ' )';
}
1;
# ABSTRACT: Guts of Tuple structured type
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Library::Structured::Tuple - Guts of Tuple structured type
=head1 VERSION
version 0.46
=head1 DESCRIPTION
There are no user facing parts here.
=for Pod::Coverage .*
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut