line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Data::Sah::Coerce; |
2
|
|
|
|
|
|
|
|
3
|
7
|
|
|
7
|
|
430935
|
use 5.010001; |
|
7
|
|
|
|
|
93
|
|
4
|
7
|
|
|
7
|
|
38
|
use strict; |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
157
|
|
5
|
7
|
|
|
7
|
|
34
|
use warnings; |
|
7
|
|
|
|
|
12
|
|
|
7
|
|
|
|
|
225
|
|
6
|
7
|
|
|
7
|
|
56
|
no warnings 'once'; |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
263
|
|
7
|
7
|
|
|
7
|
|
12694
|
use Log::ger; |
|
7
|
|
|
|
|
424
|
|
|
7
|
|
|
|
|
42
|
|
8
|
|
|
|
|
|
|
|
9
|
7
|
|
|
7
|
|
5031
|
use Data::Sah::CoerceCommon; |
|
7
|
|
|
|
|
22
|
|
|
7
|
|
|
|
|
282
|
|
10
|
|
|
|
|
|
|
|
11
|
7
|
|
|
7
|
|
51
|
use Exporter qw(import); |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
4723
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY |
14
|
|
|
|
|
|
|
our $DATE = '2021-11-28'; # DATE |
15
|
|
|
|
|
|
|
our $DIST = 'Data-Sah-Coerce'; # DIST |
16
|
|
|
|
|
|
|
our $VERSION = '0.052'; # VERSION |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our @EXPORT_OK = qw(gen_coercer); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our %SPEC; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our $Log_Coercer_Code = $ENV{LOG_SAH_COERCER_CODE} // 0; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
$SPEC{gen_coercer} = { |
25
|
|
|
|
|
|
|
v => 1.1, |
26
|
|
|
|
|
|
|
summary => 'Generate coercer code', |
27
|
|
|
|
|
|
|
description => <<'_', |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
This is mostly for testing. Normally the coercion rules will be used from |
30
|
|
|
|
|
|
|
<pm:Data::Sah>. |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
_ |
33
|
|
|
|
|
|
|
args => { |
34
|
|
|
|
|
|
|
%Data::Sah::CoerceCommon::gen_coercer_args, |
35
|
|
|
|
|
|
|
}, |
36
|
|
|
|
|
|
|
result_naked => 1, |
37
|
|
|
|
|
|
|
}; |
38
|
|
|
|
|
|
|
sub gen_coercer { |
39
|
19
|
|
|
19
|
1
|
1732168
|
my %args = @_; |
40
|
|
|
|
|
|
|
|
41
|
19
|
|
100
|
|
|
140
|
my $rt = $args{return_type} // 'val'; |
42
|
|
|
|
|
|
|
# old values still supported but deprecated |
43
|
19
|
50
|
|
|
|
76
|
$rt = 'bool_coerced+val' if $rt eq 'status+val'; |
44
|
19
|
50
|
|
|
|
63
|
$rt = 'bool_coerced+str_errmsg+val' if $rt eq 'status+err+val'; |
45
|
|
|
|
|
|
|
|
46
|
19
|
|
|
|
|
125
|
my $rules = Data::Sah::CoerceCommon::get_coerce_rules( |
47
|
|
|
|
|
|
|
%args, |
48
|
|
|
|
|
|
|
compiler=>'perl', |
49
|
|
|
|
|
|
|
data_term=>'$data', |
50
|
|
|
|
|
|
|
); |
51
|
|
|
|
|
|
|
|
52
|
19
|
|
|
|
|
42
|
my $code; |
53
|
19
|
100
|
|
|
|
58
|
if (@$rules) { |
54
|
18
|
|
|
|
|
42
|
my $code_require = ''; |
55
|
18
|
|
|
|
|
32
|
my %mem; |
56
|
18
|
|
|
|
|
47
|
for my $rule (@$rules) { |
57
|
59
|
100
|
|
|
|
141
|
next unless $rule->{modules}; |
58
|
47
|
|
|
|
|
67
|
for my $mod (keys %{$rule->{modules}}) { |
|
47
|
|
|
|
|
135
|
|
59
|
55
|
100
|
|
|
|
149
|
next if $mem{$mod}++; |
60
|
29
|
|
|
|
|
93
|
$code_require .= "require $mod;\n"; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
18
|
|
|
|
|
43
|
my $expr; |
65
|
18
|
|
|
|
|
44
|
for my $i (reverse 0..$#{$rules}) { |
|
18
|
|
|
|
|
53
|
|
66
|
59
|
|
|
|
|
104
|
my $rule = $rules->[$i]; |
67
|
59
|
|
|
|
|
107
|
my $prev_term; |
68
|
59
|
100
|
|
|
|
77
|
if ($i == $#{$rules}) { |
|
59
|
|
|
|
|
170
|
|
69
|
18
|
100
|
|
|
|
58
|
if ($rt eq 'val') { |
|
|
100
|
|
|
|
|
|
70
|
14
|
|
|
|
|
30
|
$prev_term = '$data'; |
71
|
|
|
|
|
|
|
} elsif ($rt eq 'bool_coerced+val') { |
72
|
3
|
|
|
|
|
5
|
$prev_term = '[undef, $data]'; |
73
|
|
|
|
|
|
|
} else { # bool_coerced+str_errmsg+val |
74
|
1
|
|
|
|
|
47
|
$prev_term = '[undef, undef, $data]'; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
} else { |
77
|
41
|
|
|
|
|
70
|
$prev_term = $expr; |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
59
|
100
|
|
|
|
149
|
if ($rt eq 'val') { |
|
|
100
|
|
|
|
|
|
81
|
49
|
100
|
|
|
|
117
|
if ($rule->{meta}{might_fail}) { |
82
|
13
|
|
|
|
|
85
|
$expr = "do { if ($rule->{expr_match}) { my \$res = $rule->{expr_coerce}; \$res->[0] ? undef : \$res->[1] } else { $prev_term } }"; |
83
|
|
|
|
|
|
|
} else { |
84
|
36
|
|
|
|
|
176
|
$expr = "($rule->{expr_match}) ? ($rule->{expr_coerce}) : $prev_term"; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
} elsif ($rt eq 'bool_coerced+val') { |
87
|
6
|
100
|
|
|
|
15
|
if ($rule->{meta}{might_fail}) { |
88
|
1
|
|
|
|
|
7
|
$expr = "do { if ($rule->{expr_match}) { my \$res = $rule->{expr_coerce}; \$res->[0] ? [1,\$res->[1]] : [1,\$res->[1]] } else { $prev_term } }"; |
89
|
|
|
|
|
|
|
} else { |
90
|
5
|
|
|
|
|
21
|
$expr = "($rule->{expr_match}) ? [1, $rule->{expr_coerce}] : $prev_term"; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
} else { # bool_coerced+str_errmsg+val |
93
|
4
|
100
|
|
|
|
11
|
if ($rule->{meta}{might_fail}) { |
94
|
1
|
|
|
|
|
8
|
$expr = "do { if ($rule->{expr_match}) { my \$res = $rule->{expr_coerce}; \$res->[0] ? [1, \$res->[0], \$res->[1]] : [1, undef, \$res->[1]] } else { $prev_term } }"; |
95
|
|
|
|
|
|
|
} else { |
96
|
3
|
|
|
|
|
13
|
$expr = "($rule->{expr_match}) ? [1, undef, $rule->{expr_coerce}] : $prev_term"; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
|
101
|
18
|
100
|
|
|
|
233
|
$code = join( |
|
|
100
|
|
|
|
|
|
102
|
|
|
|
|
|
|
"", |
103
|
|
|
|
|
|
|
$code_require, |
104
|
|
|
|
|
|
|
"sub {\n", |
105
|
|
|
|
|
|
|
" my \$data = shift;\n", |
106
|
|
|
|
|
|
|
" unless (defined \$data) {\n", |
107
|
|
|
|
|
|
|
" ", ($rt eq 'val' ? "return undef;" : |
108
|
|
|
|
|
|
|
$rt eq 'bool_coerced+val' ? "return [undef, undef];" : |
109
|
|
|
|
|
|
|
"return [undef, undef, undef];" # bool_coerced+str_errmsg+val |
110
|
|
|
|
|
|
|
), "\n", |
111
|
|
|
|
|
|
|
" }\n", |
112
|
|
|
|
|
|
|
" $expr;\n", |
113
|
|
|
|
|
|
|
"}", |
114
|
|
|
|
|
|
|
); |
115
|
|
|
|
|
|
|
} else { |
116
|
1
|
50
|
|
|
|
3
|
if ($rt eq 'val') { |
|
|
0
|
|
|
|
|
|
117
|
1
|
|
|
|
|
3
|
$code = 'sub { $_[0] }'; |
118
|
|
|
|
|
|
|
} elsif ($rt eq 'bool_coerced+val') { |
119
|
0
|
|
|
|
|
0
|
$code = 'sub { [undef, $_[0]] }'; |
120
|
|
|
|
|
|
|
} else { # bool_coerced+str_errmsg+val |
121
|
0
|
|
|
|
|
0
|
$code = 'sub { [undef, undef, $_[0]] }'; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
19
|
50
|
|
|
|
68
|
if ($Log_Coercer_Code) { |
126
|
0
|
|
|
|
|
0
|
log_trace("Coercer code (gen args: %s): %s", \%args, $code); |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
19
|
50
|
|
|
|
58
|
return $code if $args{source}; |
130
|
|
|
|
|
|
|
|
131
|
19
|
|
|
|
|
8212
|
my $coercer = eval $code; ## no critic: BuiltinFunctions::ProhibitStringyEval |
132
|
19
|
50
|
|
|
|
86
|
die if $@; |
133
|
19
|
|
|
|
|
236
|
$coercer; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
1; |
137
|
|
|
|
|
|
|
# ABSTRACT: Coercion rules for Data::Sah |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
__END__ |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=pod |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=encoding UTF-8 |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=head1 NAME |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
Data::Sah::Coerce - Coercion rules for Data::Sah |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head1 VERSION |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
This document describes version 0.052 of Data::Sah::Coerce (from Perl distribution Data-Sah-Coerce), released on 2021-11-28. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head1 SYNOPSIS |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
use Data::Sah::Coerce qw(gen_coercer); |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# a utility routine: gen_coercer |
158
|
|
|
|
|
|
|
my $c = gen_coercer( |
159
|
|
|
|
|
|
|
type => 'date', |
160
|
|
|
|
|
|
|
coerce_to => 'DateTime', |
161
|
|
|
|
|
|
|
coerce_rules => ['From_str::natural'], # explicitly enable a rule, etc. See Data::Sah::CoerceCommon's get_coerce_rules() for detailed syntax |
162
|
|
|
|
|
|
|
# return_type => 'str+val', # default is 'val' |
163
|
|
|
|
|
|
|
); |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
my $val = $c->(123); # unchanged, 123 |
166
|
|
|
|
|
|
|
my $val = $c->(1463307881); # becomes a DateTime object |
167
|
|
|
|
|
|
|
my $val = $c->("2016-05-15"); # becomes a DateTime object |
168
|
|
|
|
|
|
|
my $val = $c->("2016foo"); # unchanged, "2016foo" |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=head1 DESCRIPTION |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
This distribution contains a standard set of coercion rules for L<Data::Sah>. It |
173
|
|
|
|
|
|
|
is separated from the C<Data-Sah> distribution and can be used independently. |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
A coercion rule is put in |
176
|
|
|
|
|
|
|
C<Data::Sah::Coerce::$COMPILER::To_$TARGET_TYPE::From_$SOURCE_TYPE::DESCRIPTION> |
177
|
|
|
|
|
|
|
module, for example: L<Data::Sah::Coerce::perl::To_date::From_float::epoch> for |
178
|
|
|
|
|
|
|
converting date from integer (Unix epoch) or |
179
|
|
|
|
|
|
|
L<Data::Sah::Coerce::perl::To_date::From_str::iso8601> for converting date from |
180
|
|
|
|
|
|
|
ISO8601 strings like "2016-05-15". |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
Basically, a coercion rule will provide an expression (C<expr_match>) that |
183
|
|
|
|
|
|
|
evaluates to true when data can be coerced, and an expression (C<expr_coerce>) |
184
|
|
|
|
|
|
|
to actually coerce/convert data to the target type. This rule can be combined |
185
|
|
|
|
|
|
|
with other rules to form the final coercion code. |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
The module must contain C<meta> subroutine which must return a hashref that has |
188
|
|
|
|
|
|
|
the following keys (C<*> marks that the key is required): |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=over |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=item * v* => int (default: 1) |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
Metadata specification version. From L<DefHash>. Currently at 4. |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
History: bumped from 3 to 4 to remove C<enable_by_default> property. Now the |
197
|
|
|
|
|
|
|
list of standard (enabled-by-default) coercion rules is maintained in |
198
|
|
|
|
|
|
|
Data::Sah::Coerce itself. This allows us to skip scanning all |
199
|
|
|
|
|
|
|
Data::Sah::Coerce::* coercion modules installed on the system. Data::Sah::Coerce |
200
|
|
|
|
|
|
|
still accepts version 3; it just ignores the C<enable_by_default> property. |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
History: bumped from 2 to 3 to allow coercion expression to return error message |
203
|
|
|
|
|
|
|
explaining why coercion fails. The C<might_die> metadata property is replaced |
204
|
|
|
|
|
|
|
with C<might_fail>. When C<might_fail> is set to true, C<expr_coerce> must |
205
|
|
|
|
|
|
|
return array containing error message and coerced data, instead of just coerced |
206
|
|
|
|
|
|
|
data. |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
History: Bumped from 1 to 2 to exclude old module names. |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=item * summary => str |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
From L<DefHash>. |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=item * might_fail => bool (default: 0) |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
Whether coercion might fail, e.g. because of invalid input. If set to 1, |
217
|
|
|
|
|
|
|
C<expr_coerce> key that the C<coerce()> routine returns must be an expression |
218
|
|
|
|
|
|
|
that returns an array (envelope) of C<< (error_msg, data) >> instead of just |
219
|
|
|
|
|
|
|
coerced data. Error message should be a string that is set when coercion fails |
220
|
|
|
|
|
|
|
and explains why. Otherwise, if coercion succeeds, the error message string |
221
|
|
|
|
|
|
|
should be set to undefined value. |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
An example of a rule like this is coercing from string in the form of |
224
|
|
|
|
|
|
|
"YYYY-MM-DD" to a DateTime object. The rule might match any string in the form |
225
|
|
|
|
|
|
|
of C<< /\A(\d{4})-(\d{2})-(\d{2})\z/ >> while it might not be a valid date. |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
This is used for coercion rules that act as a data checker. |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=item * prio => int (0-100, default: 50) |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
This is to regulate the ordering of rules. The higher the number, the lower the |
232
|
|
|
|
|
|
|
priority (meaning the rule will be put further back). Rules that are |
233
|
|
|
|
|
|
|
computationally more expensive and/or match more broadly in general should be |
234
|
|
|
|
|
|
|
put further back (lower priority, higher number). |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=item * precludes => array of (str|re) |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
List the other rules or rule patterns that are precluded by this rule. Rules |
239
|
|
|
|
|
|
|
that are mutually exclusive or pure alternatives to one another (e.g. date |
240
|
|
|
|
|
|
|
coercien rules |
241
|
|
|
|
|
|
|
L<From_str::natural|Data::Sah::Coerce::To_date::From_str::natural> vs |
242
|
|
|
|
|
|
|
L<From_str::flexible|Data::Sah::Coerce::To_date::From_str::flexible> both parse |
243
|
|
|
|
|
|
|
natural language date string; there is usually little to none of usefulness in |
244
|
|
|
|
|
|
|
using both; besides, both rules match all string and dies when failing to parse |
245
|
|
|
|
|
|
|
the string. So in C<From_str::natural> rule, you'll find this metadata: |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
precludes => [qr/\A(From_str::alami(_.+)?|From_str::natural)\z/] |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
and in C<From_str::flexible> rule you'll find this metadata: |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
precludes => [qr/\A(From_str::alami(_.+)?|From_str::flexible)\z/] |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
Also note that rules which are specifically requested to be used (e.g. using |
254
|
|
|
|
|
|
|
C<x.perl.coerce_rules> attribute in Sah schema) will still be precluded. |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=back |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
The module must also contain C<coerce> subroutine which must generate the code |
259
|
|
|
|
|
|
|
for coercion. The subroutine must accept a hash of arguments (C<*> indicates |
260
|
|
|
|
|
|
|
required arguments): |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=over |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=item * data_term => str |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=item * coerce_to => str |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
Some Sah types are "abstract" and can be represented using a choice of several |
269
|
|
|
|
|
|
|
actual types in the target programming language. For example, "date" can be |
270
|
|
|
|
|
|
|
represented in Perl as an integer (Unix epoch value), or a DateTime object, or a |
271
|
|
|
|
|
|
|
Time::Moment object. |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
Not all target Sah types will need this argument. |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=back |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
The C<coerce> subroutine must return a hashref with the following keys (C<*> |
278
|
|
|
|
|
|
|
indicates required keys): |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
=over |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
=item * expr_match => str |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
Expression in the target language to test whether the data can be coerced. For |
285
|
|
|
|
|
|
|
example, in C<Data::Sah::Coerce::perl::To_date::From_float::epoch>, only |
286
|
|
|
|
|
|
|
integers ranging from 10^8 to 2^31 are converted into date. Non-integers or |
287
|
|
|
|
|
|
|
integers outside this range are not coerced. |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=item * expr_coerce => str |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
Expression in the target language to actually convert data to the target type. |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=item * modules => hash |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
A list of modules required by the expressions. |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=back |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
Basically, the C<coerce> subroutine must generates a code that accepts a |
300
|
|
|
|
|
|
|
non-undef data and must convert this data to the desired type/format under the |
301
|
|
|
|
|
|
|
right condition. The code to match the right condition must be put in |
302
|
|
|
|
|
|
|
C<expr_match> and the code to convert data must be put in C<expr_coerce>. |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
Program/library that uses Data::Sah::Coerce can collect rules from the rule |
305
|
|
|
|
|
|
|
modules then compose them into the final code, something like (in pseudocode): |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
if (data is undef) { |
308
|
|
|
|
|
|
|
return undef; |
309
|
|
|
|
|
|
|
} elsif (data matches expr-match-from-rule1) { |
310
|
|
|
|
|
|
|
return expr-coerce-from-rule1; |
311
|
|
|
|
|
|
|
} elsif (data matches expr-match-from-rule2) { |
312
|
|
|
|
|
|
|
return expr-coerce-from-rule1; |
313
|
|
|
|
|
|
|
... |
314
|
|
|
|
|
|
|
} else { |
315
|
|
|
|
|
|
|
# does not match any expr-match |
316
|
|
|
|
|
|
|
return original data; |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=head1 VARIABLES |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
=head2 $Log_Coercer_Code => bool (default: from ENV or 0) |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
If set to true, will log the generated coercer code (currently using L<Log::ger> |
324
|
|
|
|
|
|
|
at trace level). To see the log message, e.g. to the screen, you can use |
325
|
|
|
|
|
|
|
something like: |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
% TRACE=1 perl -MLog::ger::LevelFromEnv -MLog::ger::Output=Screen \ |
328
|
|
|
|
|
|
|
-MData::Sah::Coerce=gen_coercer -E'my $c = gen_coercer(...)' |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=head1 FUNCTIONS |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=head2 gen_coercer |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
Usage: |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
gen_coercer(%args) -> any |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
Generate coercer code. |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
This is mostly for testing. Normally the coercion rules will be used from |
342
|
|
|
|
|
|
|
L<Data::Sah>. |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
This function is not exported by default, but exportable. |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
Arguments ('*' denotes required arguments): |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=over 4 |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=item * B<coerce_rules> => I<array[str]> |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
A specification of coercion rules to use (or avoid). |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
This setting is used to specify which coercion rules to use (or avoid) in a |
355
|
|
|
|
|
|
|
flexible way. Each element is a string, in the form of either C<NAME> to mean |
356
|
|
|
|
|
|
|
specifically include a rule, or C<!NAME> to exclude a rule. |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
Some coercion modules are used by default, unless explicitly avoided using the |
359
|
|
|
|
|
|
|
'!NAME' rule. |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
To not use any rules: |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
To use the default rules plus R1 and R2: |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
['R1', 'R2'] |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
To use the default rules but not R1 and R2: |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
['!R1', '!R2'] |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=item * B<coerce_to> => I<str> |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
Some Sah types, like C<date>, can be represented in a choice of types in the |
374
|
|
|
|
|
|
|
target language. For example, in Perl you can store it as a floating number |
375
|
|
|
|
|
|
|
a.k.a. C<float(epoch)>, or as a L<DateTime> object, or L<Time::Moment> |
376
|
|
|
|
|
|
|
object. Storing in DateTime can be convenient for date manipulation but requires |
377
|
|
|
|
|
|
|
an overhead of loading the module and storing in a bulky format. The choice is |
378
|
|
|
|
|
|
|
yours to make, via this setting. |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=item * B<return_type> => I<str> (default: "val") |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
C<val> means the coercer will return the input (possibly) coerced or undef if |
383
|
|
|
|
|
|
|
coercion fails. |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
C<bool_coerced+val> means the coercer will return a 2-element array. The first |
386
|
|
|
|
|
|
|
element is a bool value set to 1 if coercion has been performed or 0 if |
387
|
|
|
|
|
|
|
otherwise. The second element is the (possibly) coerced input. |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
C<bool_coerced+str_errmsg+val> means the coercer will return a 3-element array. |
390
|
|
|
|
|
|
|
The first element is a bool value set to 1 if coercion has been performed or 0 |
391
|
|
|
|
|
|
|
if otherwise. The second element is the error message string which will be set |
392
|
|
|
|
|
|
|
if there is a failure in coercion (or undef if coercion is successful). The |
393
|
|
|
|
|
|
|
third element is the (possibly) coerced input. |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=item * B<source> => I<bool> |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
If set to true, will return coercer source code string instead of compiled code. |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
=item * B<type>* => I<sah::type_name> |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
=back |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
Return value: (any) |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
=head1 ENVIRONMENT |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
=head2 LOG_SAH_COERCER_CODE => bool |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
Set default for C<$Log_Coercer_Code>. |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=head1 HOMEPAGE |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
Please visit the project's homepage at L<https://metacpan.org/release/Data-Sah-Coerce>. |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
=head1 SOURCE |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
Source repository is at L<https://github.com/perlancar/perl-Data-Sah-Coerce>. |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=head1 SEE ALSO |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
L<Data::Sah::CoerceCommon> for detailed syntax of coerce rules (explicitly |
423
|
|
|
|
|
|
|
including/excluding rules etc). |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
L<Data::Sah> |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
L<Data::Sah::CoerceJS> |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
L<App::SahUtils>, including L<coerce-with-sah> to conveniently test coercion |
430
|
|
|
|
|
|
|
from the command-line. |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=head1 AUTHOR |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
perlancar <perlancar@cpan.org> |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=head1 CONTRIBUTING |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
To contribute, you can send patches by email/via RT, or send pull requests on |
440
|
|
|
|
|
|
|
GitHub. |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
Most of the time, you don't need to build the distribution yourself. You can |
443
|
|
|
|
|
|
|
simply modify the code, then test via: |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
% prove -l |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
If you want to build the distribution (e.g. to try to install it locally on your |
448
|
|
|
|
|
|
|
system), you can install L<Dist::Zilla>, |
449
|
|
|
|
|
|
|
L<Dist::Zilla::PluginBundle::Author::PERLANCAR>, and sometimes one or two other |
450
|
|
|
|
|
|
|
Dist::Zilla plugin and/or Pod::Weaver::Plugin. Any additional steps required |
451
|
|
|
|
|
|
|
beyond that are considered a bug and can be reported to me. |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
This software is copyright (c) 2021, 2020, 2019, 2018, 2017, 2016 by perlancar <perlancar@cpan.org>. |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
458
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=head1 BUGS |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Sah-Coerce> |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
When submitting a bug or request, please include a test-file or a |
465
|
|
|
|
|
|
|
patch to an existing test-file that illustrates the bug or desired |
466
|
|
|
|
|
|
|
feature. |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
=cut |