File Coverage

blib/lib/Specio/Library/Builtins.pm
Criterion Covered Total %
statement 36 37 97.3
branch n/a
condition n/a
subroutine 12 12 100.0
pod n/a
total 48 49 97.9


line stmt bran cond sub pod time code
1             package Specio::Library::Builtins;
2              
3 32     32   559126 use strict;
  32         67  
  32         1127  
4 32     32   156 use warnings;
  32         59  
  32         2191  
5              
6             our $VERSION = '0.53';
7              
8 32     32   7605 use parent 'Specio::Exporter';
  32         5281  
  32         210  
9              
10 32     32   2008 use List::Util 1.33 ();
  32         2474  
  32         691  
11 32     32   163 use overload ();
  32         84  
  32         650  
12 32     32   153 use re ();
  32         54  
  32         403  
13 32     32   111 use Scalar::Util ();
  32         47  
  32         503  
14 32     32   14686 use Specio::Constraint::Parameterizable;
  32         119  
  32         1587  
15 32     32   14626 use Specio::Declare;
  32         104  
  32         251  
16 32     32   171 use Specio::Helpers ();
  32         66  
  32         2730  
17              
18 0         0 BEGIN {
19 32     32   107 local $@ = undef;
20             my $has_ref_util
21 32         76 = eval { require Ref::Util; Ref::Util->VERSION('0.112'); 1 };
  32         17181  
  32         86113  
  32         49104  
22 256     256   996 sub _HAS_REF_UTIL () {$has_ref_util}
23             }
24              
25             declare(
26             'Item',
27             inline => sub {'1'}
28             );
29              
30             declare(
31             'Undef',
32             parent => t('Item'),
33             inline => sub {
34             '!defined(' . $_[1] . ')';
35             }
36             );
37              
38             declare(
39             'Defined',
40             parent => t('Item'),
41             inline => sub {
42             'defined(' . $_[1] . ')';
43             }
44             );
45              
46             declare(
47             'Bool',
48             parent => t('Item'),
49             inline => sub {
50             return sprintf( <<'EOF', ( $_[1] ) x 7 );
51             (
52             (
53             !ref( %s )
54             && (
55             !defined( %s )
56             || %s eq q{}
57             || %s eq '1'
58             || %s eq '0'
59             )
60             )
61             ||
62             (
63             Scalar::Util::blessed( %s )
64             && defined overload::Method( %s, 'bool' )
65             )
66             )
67             EOF
68             }
69             );
70              
71             declare(
72             'Value',
73             parent => t('Defined'),
74             inline => sub {
75             $_[0]->parent->inline_check( $_[1] ) . ' && !ref(' . $_[1] . ')';
76             }
77             );
78              
79             declare(
80             'Ref',
81             parent => t('Defined'),
82              
83             # no need to call parent - ref also checks for definedness
84             inline => sub { 'ref(' . $_[1] . ')' }
85             );
86              
87             declare(
88             'Str',
89             parent => t('Value'),
90             inline => sub {
91             return sprintf( <<'EOF', ( $_[1] ) x 6 );
92             (
93             (
94             defined( %s )
95             && !ref( %s )
96             && (
97             ( ref( \%s ) eq 'SCALAR' )
98             || do { ( ref( \( my $val = %s ) ) eq 'SCALAR' ) }
99             )
100             )
101             ||
102             (
103             Scalar::Util::blessed( %s )
104             && defined overload::Method( %s, q{""} )
105             )
106             )
107             EOF
108             }
109             );
110              
111             declare(
112             'Num',
113             parent => t('Str'),
114             inline => sub {
115             return sprintf( <<'EOF', ( $_[1] ) x 5 );
116             (
117             (
118             defined( %s )
119             && !ref( %s )
120             && (
121             do {
122             ( my $val = %s ) =~
123             /\A
124             -?[0-9]+(?:\.[0-9]+)?
125             (?:[Ee][\-+]?[0-9]+)?
126             \z/x
127             }
128             )
129             )
130             ||
131             (
132             Scalar::Util::blessed( %s )
133             && defined overload::Method( %s, '0+' )
134             )
135             )
136             EOF
137             }
138             );
139              
140             declare(
141             'Int',
142             parent => t('Num'),
143             inline => sub {
144             return sprintf( <<'EOF', ( $_[1] ) x 6 );
145             (
146             (
147             defined( %s )
148             && !ref( %s )
149             && (
150             do {
151             my $val1 = %s;
152             $val1 =~ /\A-?[0-9]+(?:[Ee]\+?[0-9]+)?\z/
153             && $val1 == int($val1)
154             }
155             )
156             )
157             ||
158             (
159             Scalar::Util::blessed( %s )
160             && defined overload::Method( %s, '0+' )
161             && (
162             do {
163             my $val2 = %s + 0;
164             $val2 =~ /\A-?[0-9]+(?:[Ee]\+?[0-9]+)?\z/
165             && $val2 == int($val2)
166             }
167             )
168             )
169             )
170             EOF
171             }
172             );
173              
174             {
175             my $ref_check
176             = _HAS_REF_UTIL
177             ? 'Ref::Util::is_plain_coderef(%s)'
178             : q{ref(%s) eq 'CODE'};
179              
180             declare(
181             'CodeRef',
182             parent => t('Ref'),
183             inline => sub {
184             return sprintf( <<"EOF", ( $_[1] ) x 3 );
185             (
186             $ref_check
187             ||
188             (
189             Scalar::Util::blessed( %s )
190             && defined overload::Method( %s, '&{}' )
191             )
192             )
193             EOF
194             }
195             );
196             }
197              
198             {
199             # This is a 5.8 back-compat shim stolen from Type::Tiny's Devel::Perl58Compat
200             # module.
201             unless ( exists &re::is_regexp || _HAS_REF_UTIL ) {
202             require B;
203             *re::is_regexp = sub {
204             ## no critic (ErrorHandling::RequireCheckingReturnValueOfEval)
205             eval { B::svref_2object( $_[0] )->MAGIC->TYPE eq 'r' };
206             };
207             }
208              
209             my $ref_check
210             = _HAS_REF_UTIL
211             ? 'Ref::Util::is_regexpref(%s)'
212             : 're::is_regexp(%s)';
213              
214             declare(
215             'RegexpRef',
216             parent => t('Ref'),
217             inline => sub {
218             return sprintf( <<"EOF", ( $_[1] ) x 3 );
219             (
220             $ref_check
221             ||
222             (
223             Scalar::Util::blessed( %s )
224             && defined overload::Method( %s, 'qr' )
225             )
226             )
227             EOF
228             },
229             );
230             }
231              
232             {
233             my $ref_check
234             = _HAS_REF_UTIL
235             ? 'Ref::Util::is_plain_globref(%s)'
236             : q{ref( %s ) eq 'GLOB'};
237              
238             declare(
239             'GlobRef',
240             parent => t('Ref'),
241             inline => sub {
242             return sprintf( <<"EOF", ( $_[1] ) x 3 );
243             (
244             $ref_check
245             ||
246             (
247             Scalar::Util::blessed( %s )
248             && defined overload::Method( %s, '*{}' )
249             )
250             )
251             EOF
252             }
253             );
254             }
255              
256             {
257             my $ref_check
258             = _HAS_REF_UTIL
259             ? 'Ref::Util::is_plain_globref(%s)'
260             : q{ref( %s ) eq 'GLOB'};
261              
262             # NOTE: scalar filehandles are GLOB refs, but a GLOB ref is not always a
263             # filehandle
264             declare(
265             'FileHandle',
266             parent => t('Ref'),
267             inline => sub {
268             return sprintf( <<"EOF", ( $_[1] ) x 6 );
269             (
270             (
271             $ref_check
272             && Scalar::Util::openhandle( %s )
273             )
274             ||
275             (
276             Scalar::Util::blessed( %s )
277             &&
278             (
279             %s->isa('IO::Handle')
280             ||
281             (
282             defined overload::Method( %s, '*{}' )
283             && Scalar::Util::openhandle( *{ %s } )
284             )
285             )
286             )
287             )
288             EOF
289             }
290             );
291             }
292              
293             {
294             my $ref_check
295             = _HAS_REF_UTIL
296             ? 'Ref::Util::is_blessed_ref(%s)'
297             : 'Scalar::Util::blessed(%s)';
298              
299             declare(
300             'Object',
301             parent => t('Ref'),
302             inline => sub { sprintf( $ref_check, $_[1] ) },
303             );
304             }
305              
306             declare(
307             'ClassName',
308             parent => t('Str'),
309             inline => sub {
310             return
311             sprintf(
312             <<'EOF', $_[0]->parent->inline_check( $_[1] ), ( $_[1] ) x 2 );
313             (
314             ( %s )
315             && length "%s"
316             && Specio::Helpers::is_class_loaded( "%s" )
317             )
318             EOF
319             },
320             );
321              
322             {
323             my $ref_check
324             = _HAS_REF_UTIL
325             ? 'Ref::Util::is_plain_scalarref(%s) || Ref::Util::is_plain_refref(%s)'
326             : q{ref( %s ) eq 'SCALAR' || ref( %s ) eq 'REF'};
327              
328             my $base_scalarref_check = sub {
329             return sprintf( <<"EOF", ( $_[0] ) x 4 );
330             (
331             (
332             $ref_check
333             )
334             ||
335             (
336             Scalar::Util::blessed( %s )
337             && defined overload::Method( %s, '\${}' )
338             )
339             )
340             EOF
341             };
342              
343             declare(
344             'ScalarRef',
345             type_class => 'Specio::Constraint::Parameterizable',
346             parent => t('Ref'),
347             inline => sub { $base_scalarref_check->( $_[1] ) },
348             parameterized_inline_generator => sub {
349             shift;
350             my $parameter = shift;
351             my $val = shift;
352              
353             return sprintf(
354             '( ( %s ) && ( %s ) )',
355             $base_scalarref_check->($val),
356             $parameter->inline_check( '${' . $val . '}' ),
357             );
358             }
359             );
360             }
361              
362             {
363             my $ref_check
364             = _HAS_REF_UTIL
365             ? 'Ref::Util::is_plain_arrayref(%s)'
366             : q{ref( %s ) eq 'ARRAY'};
367              
368             my $base_arrayref_check = sub {
369             return sprintf( <<"EOF", ( $_[0] ) x 3 );
370             (
371             $ref_check
372             ||
373             (
374             Scalar::Util::blessed( %s )
375             && defined overload::Method( %s, '\@{}' )
376             )
377             )
378             EOF
379             };
380              
381             declare(
382             'ArrayRef',
383             type_class => 'Specio::Constraint::Parameterizable',
384             parent => t('Ref'),
385             inline => sub { $base_arrayref_check->( $_[1] ) },
386             parameterized_inline_generator => sub {
387             shift;
388             my $parameter = shift;
389             my $val = shift;
390              
391             return sprintf(
392             '( ( %s ) && ( List::Util::all { %s } @{ %s } ) )',
393             $base_arrayref_check->($val),
394             $parameter->inline_check('$_'),
395             $val,
396             );
397             }
398             );
399             }
400              
401             {
402             my $ref_check
403             = _HAS_REF_UTIL
404             ? 'Ref::Util::is_plain_hashref(%s)'
405             : q{ref( %s ) eq 'HASH'};
406              
407             my $base_hashref_check = sub {
408             return sprintf( <<"EOF", ( $_[0] ) x 3 );
409             (
410             $ref_check
411             ||
412             (
413             Scalar::Util::blessed( %s )
414             && defined overload::Method( %s, '%%{}' )
415             )
416             )
417             EOF
418             };
419              
420             declare(
421             'HashRef',
422             type_class => 'Specio::Constraint::Parameterizable',
423             parent => t('Ref'),
424             inline => sub { $base_hashref_check->( $_[1] ) },
425             parameterized_inline_generator => sub {
426             shift;
427             my $parameter = shift;
428             my $val = shift;
429              
430             return sprintf(
431             '( ( %s ) && ( List::Util::all { %s } values %%{ %s } ) )',
432             $base_hashref_check->($val),
433             $parameter->inline_check('$_'),
434             $val,
435             );
436             }
437             );
438             }
439              
440             declare(
441             'Maybe',
442             type_class => 'Specio::Constraint::Parameterizable',
443             parent => t('Item'),
444             inline => sub {'1'},
445             parameterized_inline_generator => sub {
446             shift;
447             my $parameter = shift;
448             my $val = shift;
449              
450             return sprintf( <<'EOF', $val, $parameter->inline_check($val) );
451             ( !defined( %s ) || ( %s ) )
452             EOF
453             },
454             );
455              
456             1;
457              
458             # ABSTRACT: Implements type constraint objects for Perl's built-in types
459              
460             __END__
461              
462             =pod
463              
464             =encoding UTF-8
465              
466             =head1 NAME
467              
468             Specio::Library::Builtins - Implements type constraint objects for Perl's built-in types
469              
470             =head1 VERSION
471              
472             version 0.53
473              
474             =head1 DESCRIPTION
475              
476             This library provides a set of types parallel to those provided by Moose.
477              
478             The types are in the following hierarchy
479              
480             Item
481             Bool
482             Maybe (of `a)
483             Undef
484             Defined
485             Value
486             Str
487             Num
488             Int
489             ClassName
490             Ref
491             ScalarRef (of `a)
492             ArrayRef (of `a)
493             HashRef (of `a)
494             CodeRef
495             RegexpRef
496             GlobRef
497             FileHandle
498             Object
499              
500             =head2 Item
501              
502             Accepts any value
503              
504             =head2 Bool
505              
506             Accepts a non-reference that is C<undef>, an empty string, C<0>, or C<1>. It
507             also accepts any object which overloads boolification.
508              
509             =head2 Maybe (of `a)
510              
511             A parameterizable type which accepts C<undef> or the type C<`a>. If not
512             parameterized this type will accept any value.
513              
514             =head2 Undef
515              
516             Only accepts C<undef>.
517              
518             =head2 Value
519              
520             Accepts any non-reference value.
521              
522             =head2 Str
523              
524             Accepts any non-reference value or an object which overloads stringification.
525              
526             =head2 Num
527              
528             Accepts nearly the same values as C<Scalar::Util::looks_like_number>, but does
529             not accept numbers with leading or trailing spaces, infinities, or NaN. Also
530             accepts an object which overloads numification.
531              
532             =head2 Int
533              
534             Accepts any integer value, or an object which overloads numification and
535             numifies to an integer.
536              
537             =head2 ClassName
538              
539             Accepts any value which passes C<Str> where the string is a loaded package.
540              
541             =head2 Ref
542              
543             Accepts any reference.
544              
545             =head2 ScalarRef (of `a)
546              
547             Accepts a scalar reference or an object which overloads scalar dereferencing.
548             If parameterized, the dereferenced value must be of type C<`a>.
549              
550             =head2 ArrayRef (of `a)
551              
552             Accepts a array reference or an object which overloads array dereferencing. If
553             parameterized, the values in the arrayref must be of type C<`a>.
554              
555             =head2 HashRef (of `a)
556              
557             Accepts a hash reference or an object which overloads hash dereferencing. If
558             parameterized, the values in the hashref must be of type C<`a>.
559              
560             =head2 CodeRef
561              
562             Accepts a code (sub) reference or an object which overloads code dereferencing.
563              
564             =head2 RegexpRef
565              
566             Accepts a regex object created by C<qr//> or an object which overloads regex
567             interpolation.
568              
569             =head2 GlobRef
570              
571             Accepts a glob reference or an object which overloads glob dereferencing.
572              
573             =head2 FileHandle
574              
575             Accepts a glob reference which is an open file handle, any C<IO::Handle> Object
576             or subclass, or an object which overloads glob dereferencing and returns a glob
577             reference which is an open file handle.
578              
579             =head2 Object
580              
581             Accepts any blessed object.
582              
583             =head1 SUPPORT
584              
585             Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
586              
587             =head1 SOURCE
588              
589             The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
590              
591             =head1 AUTHOR
592              
593             Dave Rolsky <autarch@urth.org>
594              
595             =head1 COPYRIGHT AND LICENSE
596              
597             This software is Copyright (c) 2012 - 2025 by Dave Rolsky.
598              
599             This is free software, licensed under:
600              
601             The Artistic License 2.0 (GPL Compatible)
602              
603             The full text of the license can be found in the
604             F<LICENSE> file included with this distribution.
605              
606             =cut