File Coverage

blib/lib/Types/Common/String.pm
Criterion Covered Total %
statement 25 25 100.0
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 34 34 100.0


line stmt bran cond sub pod time code
1             package Types::Common::String;
2              
3 46     46   136494 use 5.008001;
  46         361  
4 46     46   493 use strict;
  46         124  
  46         1543  
5 46     46   466 use warnings;
  46         118  
  46         3195  
6 46     46   25636 use utf8;
  46         15173  
  46         356  
7              
8             BEGIN {
9 46     46   3033 $Types::Common::String::AUTHORITY = 'cpan:TOBYINK';
10 46         3604 $Types::Common::String::VERSION = '2.010001';
11             }
12              
13             $Types::Common::String::VERSION =~ tr/_//d;
14              
15 46         574 use Type::Library -base, -declare => qw(
16             SimpleStr
17             NonEmptySimpleStr
18             NumericCode
19             LowerCaseSimpleStr
20             UpperCaseSimpleStr
21             Password
22             StrongPassword
23             NonEmptyStr
24             LowerCaseStr
25             UpperCaseStr
26             StrLength
27             DelimitedStr
28 46     46   9607 );
  46         148  
29              
30 46     46   4924 use Type::Tiny ();
  46         101  
  46         1207  
31 46     46   231 use Types::TypeTiny ();
  46         87  
  46         1335  
32 46     46   12337 use Types::Standard qw( Str );
  46         212  
  46         540  
33              
34             my $meta = __PACKAGE__->meta;
35              
36             $meta->add_type(
37             name => SimpleStr,
38             parent => Str,
39             constraint => sub { length( $_ ) <= 255 and not /\n/ },
40             inlined => sub { undef, qq(length($_) <= 255), qq($_ !~ /\\n/) },
41             message => sub { "Must be a single line of no more than 255 chars" },
42             type_default => sub { return ''; },
43             );
44              
45             $meta->add_type(
46             name => NonEmptySimpleStr,
47             parent => SimpleStr,
48             constraint => sub { length( $_ ) > 0 },
49             inlined => sub { undef, qq(length($_) > 0) },
50             message => sub { "Must be a non-empty single line of no more than 255 chars" },
51             );
52              
53             $meta->add_type(
54             name => NumericCode,
55             parent => NonEmptySimpleStr,
56             constraint => sub { /^[0-9]+$/ },
57             inlined => sub { SimpleStr->inline_check( $_ ), qq($_ =~ m/^[0-9]+\$/) },
58             message => sub {
59             'Must be a non-empty single line of no more than 255 chars that consists '
60             . 'of numeric characters only';
61             },
62             );
63              
64             NumericCode->coercion->add_type_coercions(
65             NonEmptySimpleStr,
66             q[ do { (my $code = $_) =~ s/[[:punct:][:space:]]//g; $code } ],
67             );
68              
69             $meta->add_type(
70             name => Password,
71             parent => NonEmptySimpleStr,
72             constraint => sub { length( $_ ) > 3 },
73             inlined => sub { SimpleStr->inline_check( $_ ), qq(length($_) > 3) },
74             message => sub { "Must be between 4 and 255 chars" },
75             );
76              
77             $meta->add_type(
78             name => StrongPassword,
79             parent => Password,
80             constraint => sub { length( $_ ) > 7 and /[^a-zA-Z]/ },
81             inlined => sub {
82             SimpleStr()->inline_check( $_ ), qq(length($_) > 7), qq($_ =~ /[^a-zA-Z]/);
83             },
84             message => sub {
85             "Must be between 8 and 255 chars, and contain a non-alpha char";
86             },
87             );
88              
89             my ( $nestr );
90             if ( Type::Tiny::_USE_XS ) {
91             $nestr = Type::Tiny::XS::get_coderef_for( 'NonEmptyStr' );
92             }
93              
94             $meta->add_type(
95             name => NonEmptyStr,
96             parent => Str,
97             constraint => sub { length( $_ ) > 0 },
98             inlined => sub {
99             if ( $nestr ) {
100             my $xsub = Type::Tiny::XS::get_subname_for( $_[0]->name );
101             return "$xsub($_[1])" if $xsub && !$Type::Tiny::AvoidCallbacks;
102             }
103             undef, qq(length($_) > 0);
104             },
105             message => sub { "Must not be empty" },
106             $nestr ? ( compiled_type_constraint => $nestr ) : (),
107             );
108              
109             $meta->add_type(
110             name => LowerCaseStr,
111             parent => NonEmptyStr,
112             constraint => sub { !/\p{Upper}/ms },
113             inlined => sub { undef, qq($_ !~ /\\p{Upper}/ms) },
114             message => sub { "Must not contain upper case letters" },
115             );
116              
117             LowerCaseStr->coercion->add_type_coercions(
118             NonEmptyStr, q[ lc($_) ],
119             );
120              
121             $meta->add_type(
122             name => UpperCaseStr,
123             parent => NonEmptyStr,
124             constraint => sub { !/\p{Lower}/ms },
125             inlined => sub { undef, qq($_ !~ /\\p{Lower}/ms) },
126             message => sub { "Must not contain lower case letters" },
127             );
128              
129             UpperCaseStr->coercion->add_type_coercions(
130             NonEmptyStr, q[ uc($_) ],
131             );
132              
133             $meta->add_type(
134             name => LowerCaseSimpleStr,
135             parent => NonEmptySimpleStr,
136             constraint => sub { !/\p{Upper}/ms },
137             inlined => sub { undef, qq($_ !~ /\\p{Upper}/ms) },
138             message => sub { "Must not contain upper case letters" },
139             );
140              
141             LowerCaseSimpleStr->coercion->add_type_coercions(
142             NonEmptySimpleStr, q[ lc($_) ],
143             );
144              
145             $meta->add_type(
146             name => UpperCaseSimpleStr,
147             parent => NonEmptySimpleStr,
148             constraint => sub { !/\p{Lower}/ms },
149             inlined => sub { undef, qq($_ !~ /\\p{Lower}/ms) },
150             message => sub { "Must not contain lower case letters" },
151             );
152              
153             UpperCaseSimpleStr->coercion->add_type_coercions(
154             NonEmptySimpleStr, q[ uc($_) ],
155             );
156              
157             $meta->add_type(
158             name => StrLength,
159             parent => Str,
160             constraint_generator => sub {
161             return $meta->get_type( 'StrLength' ) unless @_;
162            
163             Type::Tiny::check_parameter_count_for_parameterized_type( 'Types::Common::String', "StrLength", \@_, 2 );
164             my ( $min, $max ) = @_;
165             Types::Standard::is_Int( $_ )
166             || Types::Standard::_croak(
167             "Parameters for StrLength[`min, `max] expected to be integers; got $_" )
168             for @_;
169            
170             if ( defined $max ) {
171             return sub { length( $_[0] ) >= $min and length( $_[0] ) <= $max };
172             }
173             else {
174             return sub { length( $_[0] ) >= $min };
175             }
176             },
177             inline_generator => sub {
178             my ( $min, $max ) = @_;
179            
180             return sub {
181             my $v = $_[1];
182             my @code = ( undef ); # parent constraint
183             push @code, "length($v) >= $min";
184             push @code, "length($v) <= $max" if defined $max;
185             return @code;
186             };
187             },
188             deep_explanation => sub {
189             my ( $type, $value, $varname ) = @_;
190             my ( $min, $max ) = @{ $type->parameters || [] };
191             my @whines;
192             if ( defined $max ) {
193             push @whines, sprintf(
194             '"%s" expects length(%s) to be between %d and %d',
195             $type,
196             $varname,
197             $min,
198             $max,
199             );
200             }
201             else {
202             push @whines, sprintf(
203             '"%s" expects length(%s) to be at least %d',
204             $type,
205             $varname,
206             $min,
207             );
208             }
209             push @whines, sprintf(
210             "length(%s) is %d",
211             $varname,
212             length( $value ),
213             );
214             return \@whines;
215             },
216             );
217              
218             $meta->add_type(
219             name => DelimitedStr,
220             parent => Str,
221             type_default => undef,
222             constraint_generator => sub {
223             return $meta->get_type( 'DelimitedStr' ) unless @_;
224            
225             Type::Tiny::check_parameter_count_for_parameterized_type( 'Types::Common::String', "DelimitedStr", \@_, 5 );
226             my ( $delimiter, $part_constraint, $min_parts, $max_parts, $ws ) = @_;
227            
228             Types::Standard::assert_Str( $delimiter );
229             Types::TypeTiny::assert_TypeTiny( $part_constraint )
230             if defined $part_constraint;
231             $min_parts ||= 0;
232             my $q_delimiter = $ws
233             ? sprintf( '\s*%s\s*', quotemeta( $delimiter ) )
234             : quotemeta( $delimiter );
235            
236             return sub {
237             my @split = $ws
238             ? split( $q_delimiter, do { ( my $trimmed = $_[0] ) =~ s{\A\s+|\s+\z}{}g; $trimmed } )
239             : split( $q_delimiter, $_[0] );
240             return if @split < $min_parts;
241             return if defined($max_parts) && ( @split > $max_parts );
242             !$part_constraint or $part_constraint->all( @split );
243             };
244             },
245             inline_generator => sub {
246             my ( $delimiter, $part_constraint, $min_parts, $max_parts, $ws ) = @_;
247             $min_parts ||= 0;
248             my $q_delimiter = $ws
249             ? sprintf( '\s*%s\s*', quotemeta( $delimiter ) )
250             : quotemeta( $delimiter );
251            
252             return sub {
253             my $v = $_[1];
254             my @cond;
255             push @cond, "\@\$split >= $min_parts" if $min_parts > 0;
256             push @cond, "\@\$split <= $max_parts" if defined $max_parts;
257             push @cond, Types::Standard::ArrayRef->of( $part_constraint )->inline_check( '$split' )
258             if $part_constraint && $part_constraint->{uniq} != Types::Standard::Any->{uniq};
259             return ( undef ) if ! @cond;
260             return (
261             undef,
262             sprintf(
263             'do { my $split = [ split %s, %s ]; %s }',
264             B::perlstring( $q_delimiter ),
265             $ws ? sprintf( 'do { ( my $trimmed = %s ) =~ s{\A\s+|\s+\z}{}g; $trimmed }', $v ) : $v,
266             join( q{ and }, @cond ),
267             ),
268             );
269             };
270             },
271             coercion_generator => sub {
272             my ( $parent, $self, $delimiter, $part_constraint, $min_parts, $max_parts ) = @_;
273             return unless $delimiter;
274             $part_constraint ||= Types::Standard::Str;
275             $min_parts ||= 0;
276            
277             require Type::Coercion;
278             my $c = 'Type::Coercion'->new( type_constraint => $self );
279             $c->add_type_coercions(
280             Types::Standard::ArrayRef->of(
281             $part_constraint,
282             $min_parts,
283             defined $max_parts ? $max_parts : (),
284             ),
285             sprintf( 'join( %s, @$_ )', B::perlstring( $delimiter ) ),
286             );
287             return $c;
288             },
289             );
290              
291             DelimitedStr->coercion->add_type_coercions(
292             Types::Standard::ArrayRef->of( Types::Standard::Str ),
293             'join( $", @$_ )',
294             );
295              
296             __PACKAGE__->meta->make_immutable;
297              
298             1;
299              
300             __END__