File Coverage

blib/lib/Types/Common/String.pm
Criterion Covered Total %
statement 28 28 100.0
branch n/a
condition n/a
subroutine 10 10 100.0
pod n/a
total 38 38 100.0


line stmt bran cond sub pod time code
1             package Types::Common::String;
2              
3 33     33   82826 use 5.008001;
  33         134  
4 33     33   196 use strict;
  33         79  
  33         822  
5 33     33   204 use warnings;
  33         69  
  33         925  
6 33     33   19911 use utf8;
  33         480  
  33         186  
7              
8             BEGIN {
9 33     33   1639 $Types::Common::String::AUTHORITY = 'cpan:TOBYINK';
10 33         1880 $Types::Common::String::VERSION = '2.003_000';
11             }
12              
13             $Types::Common::String::VERSION =~ tr/_//d;
14              
15 33         317 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 33     33   8072 );
  33         101  
29              
30 33     33   2658 use Type::Tiny ();
  33         89  
  33         683  
31 33     33   209 use Types::TypeTiny ();
  33         59  
  33         713  
32 33     33   10353 use Types::Standard qw( Str );
  33         110  
  33         357  
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 33     33   29231 constraint => sub { !/\p{Upper}/ms },
  33         103  
  33         586  
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             my ( $min, $max ) = @_;
164             Types::Standard::is_Int( $_ )
165             || Types::Standard::_croak(
166             "Parameters for StrLength[`min, `max] expected to be integers; got $_" )
167             for @_;
168            
169             if ( defined $max ) {
170             return sub { length( $_[0] ) >= $min and length( $_[0] ) <= $max };
171             }
172             else {
173             return sub { length( $_[0] ) >= $min };
174             }
175             },
176             inline_generator => sub {
177             my ( $min, $max ) = @_;
178            
179             return sub {
180             my $v = $_[1];
181             my @code = ( undef ); # parent constraint
182             push @code, "length($v) >= $min";
183             push @code, "length($v) <= $max" if defined $max;
184             return @code;
185             };
186             },
187             deep_explanation => sub {
188             my ( $type, $value, $varname ) = @_;
189             my ( $min, $max ) = @{ $type->parameters || [] };
190             my @whines;
191             if ( defined $max ) {
192             push @whines, sprintf(
193             '"%s" expects length(%s) to be between %d and %d',
194             $type,
195             $varname,
196             $min,
197             $max,
198             );
199             }
200             else {
201             push @whines, sprintf(
202             '"%s" expects length(%s) to be at least %d',
203             $type,
204             $varname,
205             $min,
206             );
207             }
208             push @whines, sprintf(
209             "length(%s) is %d",
210             $varname,
211             length( $value ),
212             );
213             return \@whines;
214             },
215             );
216              
217             $meta->add_type(
218             name => DelimitedStr,
219             parent => Str,
220             type_default => undef,
221             constraint_generator => sub {
222             return $meta->get_type( 'DelimitedStr' ) unless @_;
223             my ( $delimiter, $part_constraint, $min_parts, $max_parts, $ws ) = @_;
224            
225             Types::Standard::assert_Str( $delimiter );
226             Types::TypeTiny::assert_TypeTiny( $part_constraint )
227             if defined $part_constraint;
228             $min_parts ||= 0;
229             my $q_delimiter = $ws
230             ? sprintf( '\s*%s\s*', quotemeta( $delimiter ) )
231             : quotemeta( $delimiter );
232            
233             return sub {
234             my @split = $ws
235             ? split( $q_delimiter, do { ( my $trimmed = $_[0] ) =~ s{\A\s+|\s+\z}{}g; $trimmed } )
236             : split( $q_delimiter, $_[0] );
237             return if @split < $min_parts;
238             return if defined($max_parts) && ( @split > $max_parts );
239             !$part_constraint or $part_constraint->all( @split );
240             };
241             },
242             inline_generator => sub {
243             my ( $delimiter, $part_constraint, $min_parts, $max_parts, $ws ) = @_;
244             $min_parts ||= 0;
245             my $q_delimiter = $ws
246             ? sprintf( '\s*%s\s*', quotemeta( $delimiter ) )
247             : quotemeta( $delimiter );
248            
249             return sub {
250             my $v = $_[1];
251             my @cond;
252             push @cond, "\@\$split >= $min_parts" if $min_parts > 0;
253             push @cond, "\@\$split <= $max_parts" if defined $max_parts;
254             push @cond, Types::Standard::ArrayRef->of( $part_constraint )->inline_check( '$split' )
255             if $part_constraint && $part_constraint->{uniq} != Types::Standard::Any->{uniq};
256             return ( undef ) if ! @cond;
257             return (
258             undef,
259             sprintf(
260             'do { my $split = [ split %s, %s ]; %s }',
261             B::perlstring( $q_delimiter ),
262             $ws ? sprintf( 'do { ( my $trimmed = %s ) =~ s{\A\s+|\s+\z}{}g; $trimmed }', $v ) : $v,
263             join( q{ and }, @cond ),
264             ),
265             );
266             };
267             },
268             coercion_generator => sub {
269             my ( $parent, $self, $delimiter, $part_constraint, $min_parts, $max_parts ) = @_;
270             return unless $delimiter;
271             $part_constraint ||= Types::Standard::Str;
272             $min_parts ||= 0;
273            
274             require Type::Coercion;
275             my $c = 'Type::Coercion'->new( type_constraint => $self );
276             $c->add_type_coercions(
277             Types::Standard::ArrayRef->of(
278             $part_constraint,
279             $min_parts,
280             defined $max_parts ? $max_parts : (),
281             ),
282             sprintf( 'join( %s, @$_ )', B::perlstring( $delimiter ) ),
283             );
284             return $c;
285             },
286             );
287              
288             DelimitedStr->coercion->add_type_coercions(
289             Types::Standard::ArrayRef->of( Types::Standard::Str ),
290             'join( $", @$_ )',
291             );
292              
293             __PACKAGE__->meta->make_immutable;
294              
295             1;
296              
297             __END__
298              
299             =pod
300              
301             =encoding utf-8
302              
303             =head1 NAME
304              
305             Types::Common::String - drop-in replacement for MooseX::Types::Common::String
306              
307             =head1 STATUS
308              
309             This module is covered by the
310             L<Type-Tiny stability policy|Type::Tiny::Manual::Policies/"STABILITY">.
311              
312             =head1 DESCRIPTION
313              
314             A drop-in replacement for L<MooseX::Types::Common::String>.
315              
316             =head2 Types
317              
318             The following types are similar to those described in
319             L<MooseX::Types::Common::String>.
320              
321             =over
322              
323             =item *
324              
325             B<SimpleStr>
326              
327             =item *
328              
329             B<NonEmptySimpleStr>
330              
331             =item *
332              
333             B<NumericCode>
334              
335             =item *
336              
337             B<LowerCaseSimpleStr>
338              
339             =item *
340              
341             B<UpperCaseSimpleStr>
342              
343             =item *
344              
345             B<Password>
346              
347             =item *
348              
349             B<StrongPassword>
350              
351             =item *
352              
353             B<NonEmptyStr>
354              
355             =item *
356              
357             B<LowerCaseStr>
358              
359             =item *
360              
361             B<UpperCaseStr>
362              
363             =back
364              
365             This module also defines some extra type constraints not found in
366             L<MooseX::Types::Common::String>.
367              
368             =over
369              
370             =item *
371              
372             B<< StrLength[`min, `max] >>
373              
374             Type constraint for a string between min and max characters long. For
375             example:
376              
377             StrLength[4, 20]
378              
379             It is sometimes useful to combine this with another type constraint in an
380             intersection.
381              
382             (LowerCaseStr) & (StrLength[4, 20])
383              
384             The max length can be omitted.
385              
386             StrLength[10] # at least 10 characters
387              
388             Lengths are inclusive.
389              
390             =item *
391              
392             B<< DelimitedStr[`delimiter, `type, `min, `max, `ws] >>
393              
394             Parameterized constraint for delimited strings, such as comma-delimited.
395              
396             B<< DelimitedStr[",", Int, 1, 3] >> will allow between 1 and 3 integers,
397             separated by commas. So C<< "1,42,-999" >> will pass the type constraint,
398             but C<< "Hello,45" >> will fail.
399              
400             The ws parameter allows optional whitespace surrounding the delimiters,
401             as well as optional leading and trailing whitespace.
402              
403             The type, min, max, and ws paramaters are optional.
404              
405             Parameterized B<DelimitedStr> type constraints will automatically have a
406             coercion from B<< ArrayRef[`type] >> which uses C<< join >> to join by the
407             delimiter. The plain unparameterized type constraint B<DelimitedStr> has
408             a coercion from B<< ArrayRef[Str] >> which joins the strings using the
409             list separator C<< $" >> (which is a space by default).
410              
411             =back
412              
413             =head1 BUGS
414              
415             Please report any bugs to
416             L<https://github.com/tobyink/p5-type-tiny/issues>.
417              
418             =head1 SEE ALSO
419              
420             L<Types::Standard>, L<Types::Common::Numeric>.
421              
422             L<MooseX::Types::Common>,
423             L<MooseX::Types::Common::Numeric>,
424             L<MooseX::Types::Common::String>.
425              
426             =head1 AUTHOR
427              
428             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
429              
430             =head1 COPYRIGHT AND LICENCE
431              
432             This software is copyright (c) 2013-2014, 2017-2023 by Toby Inkster.
433              
434             This is free software; you can redistribute it and/or modify it under
435             the same terms as the Perl 5 programming language system itself.
436              
437             =head1 DISCLAIMER OF WARRANTIES
438              
439             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
440             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
441             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.