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         11  
  4         162  
4 4     4   23 use warnings;
  4         10  
  4         365  
5              
6             our $VERSION = '0.53';
7              
8 4     4   30 use Carp qw( confess );
  4         8  
  4         346  
9 4     4   44 use List::Util 1.33 ();
  4         119  
  4         171  
10 4     4   25 use Scalar::Util qw( blessed );
  4         9  
  4         311  
11 4     4   26 use Specio::Library::Builtins;
  4         20  
  4         34  
12 4     4   30 use Specio::TypeChecks qw( does_role );
  4         9  
  4         6181  
13              
14             my $arrayref = t('ArrayRef');
15              
16 4     4 0 21 sub parent {$arrayref}
17              
18             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
19             sub _inline {
20 1     1   12 $arrayref->inline_check( $_[1] );
21             }
22              
23             sub _parameterization_args_builder {
24 6     6   131 shift;
25 6         13 my $args = shift;
26              
27 6         14 my $saw_slurpy;
28             my $saw_optional;
29 6         14 for my $p ( @{$args} ) {
  6         19  
30 17 50       98 if ($saw_slurpy) {
31 0         0 confess
32             'A Tuple cannot have any parameters after a slurpy parameter';
33             }
34 17 50 66     66 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 17         30 my $type;
40 17 100       43 if ( blessed($p) ) {
41 13         25 $type = $p;
42             }
43             else {
44 4 50       15 if ( ref $p eq 'HASH' ) {
45 4 100       16 if ( $p->{optional} ) {
46 3         5 $saw_optional = 1;
47 3         6 $type = $p->{optional};
48             }
49 4 100       16 if ( $p->{slurpy} ) {
50 1         5 $saw_slurpy = 1;
51 1         2 $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 17 50 66     53 if ( $saw_optional && $saw_slurpy ) {
61 0         0 confess
62             'Cannot defined a slurpy Tuple with optional slots as well';
63             }
64              
65 17 50       44 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 17 50       289 confess
70             'All parameters passed to ->parameterize must be inlinable constraints'
71             unless $type->can_be_inlined;
72             }
73              
74 6         56 return ( of => $args );
75             }
76              
77             sub _name_builder {
78 6     6   31 my $self = shift;
79 6         12 my $p = shift;
80              
81 6         14 my @names;
82 6         10 for my $m ( @{ $p->{of} } ) {
  6         18  
83             ## no critic (Subroutines::ProtectPrivateSubs)
84 17 100       127 if ( blessed($m) ) {
    100          
    50          
85 13         37 push @names, $self->_name_or_anon($m);
86             }
87             elsif ( $m->{optional} ) {
88 3         9 push @names, $self->_name_or_anon( $m->{optional} ) . '?';
89             }
90             elsif ( $m->{slurpy} ) {
91 1         4 push @names, $self->_name_or_anon( $m->{slurpy} ) . '...';
92             }
93             }
94              
95 6         82 return 'Tuple[ ' . ( join ', ', @names ) . ' ]';
96             }
97              
98             sub _structured_inline_generator {
99 12     12   19 shift;
100 12         21 my $val = shift;
101 12         34 my %args = @_;
102              
103 12         16 my @of = @{ $args{of} };
  12         36  
104              
105 12         18 my $slurpy;
106             $slurpy = ( pop @of )->{slurpy}
107 12 100 100     58 if !blessed( $of[-1] ) && $of[-1]->{slurpy};
108              
109 12         45 my @code = sprintf( '( %s )', $arrayref->_inline_check($val) );
110              
111 12 100       37 unless ($slurpy) {
112 9         14 my $min = 0;
113 9         17 my $max = 0;
114 9         21 for my $p (@of) {
115              
116             # Unblessed values are optional.
117 30 100       59 if ( blessed($p) ) {
118 21         32 $min++;
119 21         34 $max++;
120             }
121             else {
122 9         14 $max++;
123             }
124             }
125              
126 9 50       22 if ($min) {
127 9         39 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         68 my $p = $of[$i];
137 36         93 my $access = sprintf( '%s->[%d]', $val, $i );
138              
139 36 100       78 if ( !blessed($p) ) {
140 9         20 my $type = $p->{optional};
141              
142 9         32 push @code,
143             sprintf(
144             '( @{%s} >= %d ? ( %s ) : 1 )', $val, $i + 1,
145             $type->_inline_check($access)
146             );
147             }
148             else {
149 27         80 push @code,
150             sprintf( '( %s )', $p->_inline_check($access) );
151             }
152             }
153              
154 12 100       29 if ($slurpy) {
155 3         4 my $non_slurpy = scalar @of;
156 3         5 my $check
157             = '( @{%s} > %d ? ( List::Util::all { %s } @{%s}[%d .. $#{%s}] ) : 1 )';
158 3         6 push @code,
159             sprintf(
160             $check,
161             $val, $non_slurpy, $slurpy->_inline_check('$_'),
162             $val, $non_slurpy, $val,
163             );
164             }
165              
166 12         811 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.53
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             =head1 SOURCE
198              
199             The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
200              
201             =head1 AUTHOR
202              
203             Dave Rolsky <autarch@urth.org>
204              
205             =head1 COPYRIGHT AND LICENSE
206              
207             This software is Copyright (c) 2012 - 2025 by Dave Rolsky.
208              
209             This is free software, licensed under:
210              
211             The Artistic License 2.0 (GPL Compatible)
212              
213             The full text of the license can be found in the
214             F<LICENSE> file included with this distribution.
215              
216             =cut