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