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 30     30   87725 use 5.008001;
  30         132  
4 30     30   178 use strict;
  30         70  
  30         767  
5 30     30   169 use warnings;
  30         75  
  30         901  
6 30     30   19206 use utf8;
  30         447  
  30         187  
7              
8             BEGIN {
9 30     30   1513 $Types::Common::String::AUTHORITY = 'cpan:TOBYINK';
10 30         1789 $Types::Common::String::VERSION = '2.002001';
11             }
12              
13             $Types::Common::String::VERSION =~ tr/_//d;
14              
15 30         302 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 30     30   7909 );
  30         76  
29              
30 30     30   2476 use Type::Tiny ();
  30         65  
  30         562  
31 30     30   162 use Types::TypeTiny ();
  30         61  
  30         733  
32 30     30   9725 use Types::Standard qw( Str );
  30         108  
  30         307  
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 30     30   26197 constraint => sub { !/\p{Upper}/ms },
  30         76  
  30         491  
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__