package Types::Standard::CycleTuple; use 5.006001; use strict; use warnings; BEGIN { $Types::Standard::CycleTuple::AUTHORITY = 'cpan:TOBYINK'; $Types::Standard::CycleTuple::VERSION = '1.004004'; } use Type::Tiny (); use Types::Standard (); use Types::TypeTiny (); sub _croak ($;@) { require Error::TypeTiny; goto \&Error::TypeTiny::croak } my $_Optional = Types::Standard::Optional; my $_arr = Types::Standard::ArrayRef; no warnings; my $cycleuniq = 0; sub __constraint_generator { my @params = map { ref($_) eq 'HASH' and exists($_->{slurpy}) and _croak("Parameters to CycleTuple[...] cannot be slurpy"); my $param = $_; Types::TypeTiny::TypeTiny->check($param) or _croak("Parameters to CycleTuple[...] expected to be type constraints; got $param"); $param; } @_; my $count = @params; my $tuple = Types::Standard::Tuple()->of(@params); _croak("Parameters to CycleTuple[...] cannot be optional") if grep !!$_->is_strictly_a_type_of($_Optional), @params; sub { my $value = shift; return unless $_arr->check($value); return if @$value % $count; my $i = 0; while ($i < $#$value) { my $tmp = [@$value[ $i .. $i+$count-1 ]]; return unless $tuple->check($tmp); $i += $count; } !!1; } } sub __inline_generator { my @params = map { my $param = $_; Types::TypeTiny::TypeTiny->check($param) or _croak("Parameter to CycleTuple[`a] expected to be a type constraint; got $param"); $param; } @_; my $count = @params; my $tuple = Types::Standard::Tuple()->of(@params); return unless $tuple->can_be_inlined; sub { $cycleuniq++; my $v = $_[1]; my @checks = $_arr->inline_check($v); push @checks, sprintf( 'not(@%s %% %d)', ($v=~/\A\$[a-z0-9_]+\z/i ? $v : "{$v}"), $count, ); push @checks, sprintf( 'do { my $cyclecount%d = 0; my $cycleok%d = 1; while ($cyclecount%d < $#{%s}) { my $cycletmp%d = [@{%s}[$cyclecount%d .. $cyclecount%d+%d]]; unless (%s) { $cycleok%d = 0; last; }; $cyclecount%d += %d; }; $cycleok%d; }', $cycleuniq, $cycleuniq, $cycleuniq, $v, $cycleuniq, $v, $cycleuniq, $cycleuniq, $count - 1, $tuple->inline_check("\$cycletmp$cycleuniq"), $cycleuniq, $cycleuniq, $count, $cycleuniq, ) if grep { $_->inline_check('$xyz') ne '(!!1)' } @params; join(' && ', @checks); } } sub __deep_explanation { my ($type, $value, $varname) = @_; my @constraints = map Types::TypeTiny::to_TypeTiny($_), @{ $type->parameters }; if (@$value % @constraints) { return [ sprintf('"%s" expects a multiple of %d values in the array', $type, scalar(@constraints)), sprintf('%d values found', scalar(@$value)), ]; } for my $i (0 .. $#$value) { my $constraint = $constraints[$i % @constraints]; next if $constraint->check($value->[$i]); return [ sprintf('"%s" constrains value at index %d of array with "%s"', $type, $i, $constraint), @{ $constraint->validate_explain($value->[$i], sprintf('%s->[%s]', $varname, $i)) }, ]; } # This should never happen... return; # uncoverable statement } my $label_counter = 0; sub __coercion_generator { my ($parent, $child, @tuple) = @_; my $child_coercions_exist = 0; my $all_inlinable = 1; for my $tc (@tuple) { $all_inlinable = 0 if !$tc->can_be_inlined; $all_inlinable = 0 if $tc->has_coercion && !$tc->coercion->can_be_inlined; $child_coercions_exist++ if $tc->has_coercion; } return unless $child_coercions_exist; my $C = "Type::Coercion"->new(type_constraint => $child); if ($all_inlinable) { $C->add_type_coercions($parent => Types::Standard::Stringable { my $label = sprintf("CTUPLELABEL%d", ++$label_counter); my $label2 = sprintf("CTUPLEINNER%d", $label_counter); my @code; push @code, 'do { my ($orig, $return_orig, $tmp, @new) = ($_, 0);'; push @code, "$label: {"; push @code, sprintf('(($return_orig = 1), last %s) if scalar(@$orig) %% %d != 0;', $label, scalar @tuple); push @code, sprintf('my $%s = 0; while ($%s < @$orig) {', $label2, $label2); for my $i (0 .. $#tuple) { my $ct = $tuple[$i]; my $ct_coerce = $ct->has_coercion; push @code, sprintf( 'do { $tmp = %s; (%s) ? ($new[$%s + %d]=$tmp) : (($return_orig=1), last %s) };', $ct_coerce ? $ct->coercion->inline_coercion("\$orig->[\$$label2 + $i]") : "\$orig->[\$$label2 + $i]", $ct->inline_check('$tmp'), $label2, $i, $label, ); } push @code, sprintf('$%s += %d;', $label2, scalar(@tuple)); push @code, '}'; push @code, '}'; push @code, '$return_orig ? $orig : \\@new'; push @code, '}'; "@code"; }); } else { $C->add_type_coercions( $parent => sub { my $value = @_ ? $_[0] : $_; if (scalar(@$value) % scalar(@tuple) != 0) { return $value; } my @new; for my $i (0 .. $#$value) { my $ct = $tuple[$i % @tuple]; my $x = $ct->has_coercion ? $ct->coerce($value->[$i]) : $value->[$i]; return $value unless $ct->check($x); $new[$i] = $x; } return \@new; }, ); }; return $C; } 1; __END__ =pod =encoding utf-8 =head1 NAME Types::Standard::CycleTuple - internals for the Types::Standard CycleTuple type constraint =head1 STATUS This module is considered part of Type-Tiny's internals. It is not covered by the L. =head1 DESCRIPTION This file contains some of the guts for L. It will be loaded on demand. You may ignore its presence. =head1 BUGS Please report any bugs to L. =head1 SEE ALSO L. =head1 AUTHOR Toby Inkster Etobyink@cpan.orgE. =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2017-2019 by Toby Inkster. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =head1 DISCLAIMER OF WARRANTIES THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.