File Coverage

blib/lib/Data/Sah/Params.pm
Criterion Covered Total %
statement 79 81 97.5
branch 30 38 78.9
condition n/a
subroutine 12 12 100.0
pod 1 4 25.0
total 122 135 90.3


line stmt bran cond sub pod time code
1             package Data::Sah::Params;
2              
3             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
4             our $DATE = '2020-09-25'; # DATE
5             our $DIST = 'Data-Sah-Params'; # DIST
6             our $VERSION = '0.005'; # VERSION
7              
8 1     1   504 use 5.010001;
  1         7  
9 1     1   4 use strict 'subs', 'vars';
  1         2  
  1         20  
10 1     1   4 use warnings;
  1         1  
  1         36  
11              
12 1     1   5 use Exporter qw(import);
  1         2  
  1         648  
13             our @EXPORT_OK = qw(compile Slurpy Optional Named);
14              
15             sub Optional($) {
16 2     2 0 3599 bless [$_[0]], "_Optional";
17             }
18              
19             sub Slurpy($) {
20 1     1 0 2761 bless [$_[0]], "_Slurpy";
21             }
22              
23             sub Named {
24 5 100   5 0 3921 @_ or die "Need at least one pair for Named";
25 4 100       18 @_ % 2 == 0 or die "Odd number of elements for Named";
26 3         14 bless {@_}, "_Named";
27             }
28              
29             sub compile {
30 7 100   7 1 4228 my $opts = ref($_[0]) eq 'HASH' ? shift : {};
31 7 50       16 die "Please specify some params" unless @_;
32              
33             # we currently use Perinci::Sub::ValidateArgs to generate the validator, so
34             # we create rinci metadata from params specification.
35 7         21 my $meta = {v=>1.1, args=>{}, result_naked=>1};
36 7 100       16 if (ref($_[0]) eq '_Named') {
37 2 100       13 die "Cannot mixed Named and other params" unless @_ == 1;
38 1         3 $meta->{args_as} = 'hash';
39 1         2 for my $arg_name (keys %{$_[0]}) {
  1         5  
40 3         5 my $arg_schema = $_[0]{$arg_name};
41 3         4 my $req = 1;
42 3         3 while (1) {
43 4         6 my $ref = ref($arg_schema);
44 4 100       8 if ($ref eq '_Optional') {
45 1         2 $req = 0;
46 1         2 $arg_schema = $arg_schema->[0];
47 1         2 next;
48             }
49 3 50       5 if ($ref eq '_Slurpy') {
50             # noop
51 0         0 $arg_schema = $arg_schema->[0];
52 0         0 next;
53             }
54 3         4 last;
55             }
56 3         8 $meta->{args}{$arg_name} = {
57             schema => $arg_schema,
58             req => $req,
59             };
60             }
61             } else {
62 5         10 $meta->{args_as} = 'array';
63 5         14 for my $pos (0..$#_) {
64 9         19 my $arg_name = "arg$pos";
65 9         11 my $arg_schema = $_[$pos];
66 9         10 my $req = 1;
67 9         10 my $slurpy;
68 9         10 while (1) {
69 11         15 my $ref = ref($arg_schema);
70 11 100       20 if ($ref eq '_Named') {
71 1         8 die "Cannot mixed Named and other params";
72             }
73 10 100       17 if ($ref eq '_Optional') {
74 1         2 $req = 0;
75 1         2 $arg_schema = $arg_schema->[0];
76 1         2 next;
77             }
78 9 100       15 if ($ref eq '_Slurpy') {
79 1 50       4 die "Slurpy parameter must be the last parameter"
80             unless $pos == $#_;
81 1         2 $slurpy = 1;
82 1         2 $arg_schema = $arg_schema->[0];
83 1         2 next;
84             }
85 8         10 last;
86             }
87 8         36 $meta->{args}{$arg_name} = {
88             schema => $arg_schema,
89             req => $req,
90             pos => $pos,
91             (greedy => $slurpy) x !!$slurpy,
92             };
93             }
94             }
95              
96 5         492 require Perinci::Sub::ValidateArgs;
97 5         3790 my $src = Perinci::Sub::ValidateArgs::gen_args_validator(
98             meta=>$meta, source=>1, die=>1);
99              
100             # do some munging
101 5 100       124047 if ($meta->{args_as} eq 'hash') {
102 1 50       14 $src =~ s/^(\s*my \$args = )shift;/${1}{\@_};/m
103             or die "BUG: Can't replace #1a";
104 1 50       10 $src =~ s/(\A.+^\s*return )undef;/${1}\$args;/ms
105             or die "BUG: Can't replace #2a";
106             } else {
107 4 50       48 $src =~ s/^(\s*my \$args = )shift;/${1}[\@_];/m
108             or die "BUG: Can't replace #1b";
109 4 50       60 $src =~ s/(\A.+^\s*return )undef;/${1}\@\$args;/ms
110             or die "BUG: Can't replace #2b";
111             }
112 5 100       28 return $src if $opts->{want_source};
113              
114 1     1   7 my $code = eval $src;
  1     1   2  
  1     1   436  
  1     1   6  
  1         1  
  1         195  
  1         6  
  1         2  
  1         424  
  1         7  
  1         2  
  1         303  
  4         281  
115             #use Eval::Closure; my $code = eval_closure(source => $src);
116 4 50       14 die if $@;
117 4         23 $code;
118             }
119              
120             1;
121             # ABSTRACT: (DEPRECATED) Validate function arguments using Sah schemas
122              
123             __END__
124              
125             =pod
126              
127             =encoding UTF-8
128              
129             =head1 NAME
130              
131             Data::Sah::Params - (DEPRECATED) Validate function arguments using Sah schemas
132              
133             =head1 VERSION
134              
135             This document describes version 0.005 of Data::Sah::Params (from Perl distribution Data-Sah-Params), released on 2020-09-25.
136              
137             =head1 SYNOPSIS
138              
139             use Data::Sah::Params qw(compile Optional Slurpy Named);
140              
141             # positional parameters, some optional
142             sub f1 {
143             state $check = compile(
144             ["str*"],
145             ["int*", min=>1, max=>10, default=>5],
146             Optional [array => of=>"int*"],
147             );
148             my ($foo, $bar, $baz) = $check->(@_);
149             ...
150             }
151             f1(); # dies, missing required argument $foo
152             f1(undef); # dies, $foo must not be undef
153             f1("a"); # dies, missing required argument $bar
154             f1("a", undef); # ok, $bar = 5, $baz = undef
155             f1("a", 1); # ok, $bar = 1, $baz = undef
156             f1("a", "x"); # dies, $bar is not an int
157             f1("a", 3, [1,2,3]); # ok
158              
159             # positional parameters, slurpy last parameter
160             sub f2 {
161             state $check = compile(
162             ["str*"],
163             ["int*", min=>1, max=>10, default=>5],
164             Slurpy [array => of=>"int*"],
165             );
166             my ($foo, $bar, $baz) = $check->(@_);
167             ...
168             }
169             f1("a", 3, 1,2,3); # ok, $foo="a", $bar=3, $baz=[1,2,3]
170             f1("a", 3, 1,2,"b"); # dies, third element of $baz not an integer
171              
172             # named parameters, some optional
173             sub f3 {
174             state $check = compile(Named
175             foo => ["str*"],
176             bar => ["int*", min=>1, max=>10, default=>5],
177             baz => Optional [array => of=>"int*"],
178             );
179             my $args = $check->(@_);
180             ...
181             }
182             f1(foo => "a"); # dies, missing argument 'bar'
183             f1(foo => "a", bar=>1); # ok
184             f1(foo => "a", bar=>1, baz=>2); # dies, baz not an array
185              
186             =head1 DESCRIPTION
187              
188             B<DEPRECATION NOTICE.> Deprecated in favor of L<Params::Sah>.
189              
190             Experimental.
191              
192             Currently mixing positional and named parameters not yet supported.
193              
194             =for Pod::Coverage ^(Optional|Slurpy|Named)$
195              
196             =head1 FUNCTIONS
197              
198             =head2 compile([ \%opts, ] $schema, ...) => coderef
199              
200             Create a validator. Accepts a list of schemas. Each schema can be prefixed with
201             C<Optional> or C<Slurpy>. Or, if your function will accept named arguments
202             (C<%args>) you can use: C<< Named(PARAM1=>$schema1, PARAM2=>$schema2, ...) >>
203             instead.
204              
205             Known options:
206              
207             =over
208              
209             =item * want_source => bool
210              
211             If set to 1, will return validator source code string instead of compiled code
212             (coderef). Useful for debugging.
213              
214             =back
215              
216             =head1 HOMEPAGE
217              
218             Please visit the project's homepage at L<https://metacpan.org/release/Data-Sah-Params>.
219              
220             =head1 SOURCE
221              
222             Source repository is at L<https://github.com/perlancar/perl-Data-Sah-Params>.
223              
224             =head1 BUGS
225              
226             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Sah-Params>
227              
228             When submitting a bug or request, please include a test-file or a
229             patch to an existing test-file that illustrates the bug or desired
230             feature.
231              
232             =head1 SEE ALSO
233              
234             L<Params::Sah> is now the preferred module over this.
235              
236             L<Sah> for the schema language.
237              
238             Similar modules: L<Type::Params>, L<Params::Validate>, L<Params::CheckCompiler>.
239              
240             If you put your schemas in L<Rinci> function metadata (I recommend it, for the
241             convenience of specifying other stuffs besides argument schemas), take a look at
242             L<Perinci::Sub::ValidateArgs>.
243              
244             L<Params::Sah>. I've actually implemented something similar the year before
245             (albeit with a slightly different interface), before absent-mindedly
246             reimplemented later :-) We'll see which one will thrive.
247              
248             =head1 AUTHOR
249              
250             perlancar <perlancar@cpan.org>
251              
252             =head1 COPYRIGHT AND LICENSE
253              
254             This software is copyright (c) 2020, 2016 by perlancar@cpan.org.
255              
256             This is free software; you can redistribute it and/or modify it under
257             the same terms as the Perl 5 programming language system itself.
258              
259             =cut