File Coverage

blib/lib/Specio/Library/Structured/Tuple.pm
Criterion Covered Total %
statement 84 88 95.4
branch 28 36 77.7
condition 7 9 77.7
subroutine 12 12 100.0
pod 0 1 0.0
total 131 146 89.7


line stmt bran cond sub pod time code
1             package Specio::Library::Structured::Tuple;
2              
3 4     4   31 use strict;
  4         10  
  4         136  
4 4     4   21 use warnings;
  4         12  
  4         184  
5              
6             our $VERSION = '0.47';
7              
8 4     4   33 use Carp qw( confess );
  4         9  
  4         204  
9 4     4   42 use List::Util ();
  4         10  
  4         77  
10 4     4   32 use Scalar::Util qw( blessed );
  4         11  
  4         239  
11 4     4   27 use Specio::Library::Builtins;
  4         9  
  4         28  
12 4     4   37 use Specio::TypeChecks qw( does_role );
  4         16  
  4         3622  
13              
14             my $arrayref = t('ArrayRef');
15              
16 4     4 0 18 sub parent {$arrayref}
17              
18             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
19             sub _inline {
20 1     1   24 $arrayref->inline_check( $_[1] );
21             }
22              
23             sub _parameterization_args_builder {
24 5     5   92 shift;
25 5         12 my $args = shift;
26              
27 5         11 my $saw_slurpy;
28             my $saw_optional;
29 5         10 for my $p ( @{$args} ) {
  5         12  
30 15 50       75 if ($saw_slurpy) {
31 0         0 confess
32             'A Tuple cannot have any parameters after a slurpy parameter';
33             }
34 15 50 66     42 if ( $saw_optional && blessed($p) ) {
35 0         0 confess
36             'A Tuple cannot have a non-optional parameter after an optional parameter';
37             }
38              
39 15         20 my $type;
40 15 100       45 if ( blessed($p) ) {
41 11         17 $type = $p;
42             }
43             else {
44 4 50       12 if ( ref $p eq 'HASH' ) {
45 4 100       18 if ( $p->{optional} ) {
46 3         6 $saw_optional = 1;
47 3         6 $type = $p->{optional};
48             }
49 4 100       12 if ( $p->{slurpy} ) {
50 1         21 $saw_slurpy = 1;
51 1         4 $type = $p->{slurpy};
52             }
53             }
54             else {
55 0         0 confess
56             'Can only pass types, optional types, and slurpy types when defining a Tuple';
57             }
58             }
59              
60 15 50 66     40 if ( $saw_optional && $saw_slurpy ) {
61 0         0 confess
62             'Cannot defined a slurpy Tuple with optional slots as well';
63             }
64              
65 15 50       34 does_role( $type, 'Specio::Constraint::Role::Interface' )
66             or confess
67             'All parameters passed to ->parameterize must be objects which do the Specio::Constraint::Role::Interface role';
68              
69 15 50       204 confess
70             'All parameters passed to ->parameterize must be inlinable constraints'
71             unless $type->can_be_inlined;
72             }
73              
74 5         42 return ( of => $args );
75             }
76              
77             sub _name_builder {
78 5     5   27 my $self = shift;
79 5         7 my $p = shift;
80              
81 5         11 my @names;
82 5         10 for my $m ( @{ $p->{of} } ) {
  5         14  
83             ## no critic (Subroutines::ProtectPrivateSubs)
84 15 100       120 if ( blessed($m) ) {
    100          
    50          
85 11         26 push @names, $self->_name_or_anon($m);
86             }
87             elsif ( $m->{optional} ) {
88 3         19 push @names, $self->_name_or_anon( $m->{optional} ) . '?';
89             }
90             elsif ( $m->{slurpy} ) {
91 1         5 push @names, $self->_name_or_anon( $m->{slurpy} ) . '...';
92             }
93             }
94              
95 5         63 return 'Tuple[ ' . ( join ', ', @names ) . ' ]';
96             }
97              
98             sub _structured_inline_generator {
99 12     12   21 shift;
100 12         24 my $val = shift;
101 12         36 my %args = @_;
102              
103 12         23 my @of = @{ $args{of} };
  12         41  
104              
105 12         20 my $slurpy;
106             $slurpy = ( pop @of )->{slurpy}
107 12 100 100     74 if !blessed( $of[-1] ) && $of[-1]->{slurpy};
108              
109 12         46 my @code = sprintf( '( %s )', $arrayref->_inline_check($val) );
110              
111 12 100       39 unless ($slurpy) {
112 9         22 my $min = 0;
113 9         15 my $max = 0;
114 9         21 for my $p (@of) {
115              
116             # Unblessed values are optional.
117 30 100       76 if ( blessed($p) ) {
118 21         29 $min++;
119 21         37 $max++;
120             }
121             else {
122 9         17 $max++;
123             }
124             }
125              
126 9 50       27 if ($min) {
127 9         40 push @code,
128             sprintf(
129             '( @{ %s } >= %d && @{ %s } <= %d )',
130             $val, $min, $val, $max
131             );
132             }
133             }
134              
135 12         40 for my $i ( 0 .. $#of ) {
136 36         69 my $p = $of[$i];
137 36         90 my $access = sprintf( '%s->[%d]', $val, $i );
138              
139 36 100       98 if ( !blessed($p) ) {
140 9         28 my $type = $p->{optional};
141              
142 9         23 push @code,
143             sprintf(
144             '( @{%s} >= %d ? ( %s ) : 1 )', $val, $i + 1,
145             $type->_inline_check($access)
146             );
147             }
148             else {
149 27         134 push @code,
150             sprintf( '( %s )', $p->_inline_check($access) );
151             }
152             }
153              
154 12 100       45 if ($slurpy) {
155 3         9 my $non_slurpy = scalar @of;
156 3         6 my $check
157             = '( @{%s} > %d ? ( List::Util::all { %s } @{%s}[%d .. $#{%s}] ) : 1 )';
158 3         11 push @code,
159             sprintf(
160             $check,
161             $val, $non_slurpy, $slurpy->_inline_check('$_'),
162             $val, $non_slurpy, $val,
163             );
164             }
165              
166 12         350 return '( ' . ( join ' && ', @code ) . ' )';
167             }
168              
169             1;
170              
171             # ABSTRACT: Guts of Tuple structured type
172              
173             __END__
174              
175             =pod
176              
177             =encoding UTF-8
178              
179             =head1 NAME
180              
181             Specio::Library::Structured::Tuple - Guts of Tuple structured type
182              
183             =head1 VERSION
184              
185             version 0.47
186              
187             =head1 DESCRIPTION
188              
189             There are no user facing parts here.
190              
191             =for Pod::Coverage .*
192              
193             =head1 SUPPORT
194              
195             Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
196              
197             I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
198              
199             =head1 SOURCE
200              
201             The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
202              
203             =head1 AUTHOR
204              
205             Dave Rolsky <autarch@urth.org>
206              
207             =head1 COPYRIGHT AND LICENSE
208              
209             This software is Copyright (c) 2012 - 2021 by Dave Rolsky.
210              
211             This is free software, licensed under:
212              
213             The Artistic License 2.0 (GPL Compatible)
214              
215             The full text of the license can be found in the
216             F<LICENSE> file included with this distribution.
217              
218             =cut