line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package TUWF::Validate::Interop; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
750
|
use strict; |
|
1
|
|
|
|
|
15
|
|
|
1
|
|
|
|
|
30
|
|
4
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
23
|
|
5
|
1
|
|
|
1
|
|
5
|
use TUWF::Validate; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
34
|
|
6
|
1
|
|
|
1
|
|
5
|
use Exporter 'import'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
25
|
|
7
|
1
|
|
|
1
|
|
5
|
use Carp 'croak'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
3188
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our @EXPORT_OK = ('analyze'); |
10
|
|
|
|
|
|
|
our $VERSION = '1.5'; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
# Analyzed ("flattened") object: |
14
|
|
|
|
|
|
|
# { type => scalar | bool | num | int | array | hash | any |
15
|
|
|
|
|
|
|
# , min, max, minlength, maxlength, required, regexes |
16
|
|
|
|
|
|
|
# , keys, values, unknown |
17
|
|
|
|
|
|
|
# } |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
sub _merge_type { |
20
|
103
|
|
|
103
|
|
144
|
my($c, $o) = @_; |
21
|
103
|
|
100
|
|
|
231
|
my $n = $c->{name}||''; |
22
|
|
|
|
|
|
|
|
23
|
103
|
50
|
33
|
|
|
306
|
return if $o->{type} eq 'int' || $o->{type} eq 'bool'; |
24
|
103
|
100
|
100
|
|
|
276
|
$o->{type} = 'int' if $n eq 'int' || $n eq 'uint'; |
25
|
103
|
100
|
66
|
|
|
390
|
$o->{type} = 'bool' if $n eq 'anybool' || $n eq 'undefbool' || $n eq 'jsonbool'; |
|
|
|
100
|
|
|
|
|
26
|
103
|
100
|
|
|
|
213
|
$o->{type} = 'num' if $n eq 'num'; |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub _merge { |
31
|
103
|
|
|
103
|
|
166
|
my($c, $o) = @_; |
32
|
|
|
|
|
|
|
|
33
|
103
|
|
|
|
|
202
|
_merge_type $c, $o; |
34
|
|
|
|
|
|
|
|
35
|
103
|
100
|
100
|
|
|
263
|
$o->{required} = 1 if ($c->{name}||'') eq 'anybool'; |
36
|
|
|
|
|
|
|
|
37
|
103
|
100
|
50
|
|
|
191
|
$o->{values} = _merge_toplevel($c->{schema}{values}, $o->{values}||{}) if $c->{schema}{values}; |
38
|
|
|
|
|
|
|
|
39
|
103
|
100
|
|
|
|
189
|
if($c->{schema}{keys}) { |
40
|
6
|
|
50
|
|
|
26
|
$o->{keys} ||= {}; |
41
|
6
|
|
50
|
|
|
11
|
$o->{keys}{$_} = _merge_toplevel($c->{schema}{keys}{$_}, $o->{keys}{$_}||{}) for keys %{$c->{schema}{keys}}; |
|
6
|
|
|
|
|
29
|
|
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
103
|
50
|
33
|
|
|
216
|
$o->{minlength} = $c->{schema}{_analyze_minlength} if defined $c->{schema}{_analyze_minlength} && (!defined $o->{minlength} || $o->{minlength} < $c->{schema}{_analyze_minlength}); |
|
|
|
66
|
|
|
|
|
45
|
103
|
50
|
33
|
|
|
234
|
$o->{maxlength} = $c->{schema}{_analyze_maxlength} if defined $c->{schema}{_analyze_maxlength} && (!defined $o->{maxlength} || $o->{maxlength} > $c->{schema}{_analyze_maxlength}); |
|
|
|
66
|
|
|
|
|
46
|
103
|
100
|
100
|
|
|
195
|
$o->{min} = $c->{schema}{_analyze_min} if defined $c->{schema}{_analyze_min} && (!defined $o->{min} || $o->{min} < $c->{schema}{_analyze_min} ); |
|
|
|
100
|
|
|
|
|
47
|
103
|
50
|
66
|
|
|
228
|
$o->{max} = $c->{schema}{_analyze_max} if defined $c->{schema}{_analyze_max} && (!defined $o->{max} || $o->{max} > $c->{schema}{_analyze_max} ); |
|
|
|
66
|
|
|
|
|
48
|
103
|
100
|
|
|
|
188
|
push @{$o->{regexes}}, $c->{schema}{_analyze_regex} if defined $c->{schema}{_analyze_regex}; |
|
25
|
|
|
|
|
62
|
|
49
|
|
|
|
|
|
|
|
50
|
103
|
|
|
|
|
138
|
_merge($_, $o) for @{$c->{validations}}; |
|
103
|
|
|
|
|
250
|
|
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
sub _merge_toplevel { |
55
|
49
|
|
|
49
|
|
90
|
my($c, $o) = @_; |
56
|
49
|
|
66
|
|
|
193
|
$o->{required} ||= $c->{schema}{required}; |
57
|
49
|
|
33
|
|
|
177
|
$o->{unknown} ||= $c->{schema}{unknown}; |
58
|
49
|
100
|
|
|
|
102
|
$o->{default} = $c->{schema}{default} if exists $c->{schema}{default}; |
59
|
49
|
50
|
33
|
|
|
145
|
$o->{type} = $c->{schema}{type} if !$o->{type} || $o->{type} eq 'any'; |
60
|
|
|
|
|
|
|
|
61
|
49
|
|
|
|
|
123
|
_merge $c, $o; |
62
|
49
|
|
|
|
|
159
|
bless $o, __PACKAGE__; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub analyze { |
67
|
41
|
|
|
41
|
0
|
63
|
my $c = shift; |
68
|
41
|
|
33
|
|
|
151
|
$c->{analysis} ||= _merge_toplevel $c, {}; |
69
|
|
|
|
|
|
|
$c->{analysis} |
70
|
41
|
|
|
|
|
113
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# Assumes that $obj already has the required format/structure, odd things may |
74
|
|
|
|
|
|
|
# happen if this is not the case. |
75
|
|
|
|
|
|
|
# unknown => remove|reject|pass |
76
|
|
|
|
|
|
|
sub coerce_for_json { |
77
|
35
|
|
|
35
|
0
|
74
|
my($o, $obj, %opt) = @_; |
78
|
35
|
|
66
|
|
|
109
|
$opt{unknown} ||= $o->{unknown}; |
79
|
35
|
100
|
|
|
|
101
|
return undef if !defined $obj; |
80
|
29
|
100
|
|
|
|
79
|
return $obj+0 if $o->{type} eq 'num'; |
81
|
27
|
100
|
|
|
|
95
|
return int $obj if $o->{type} eq 'int'; |
82
|
22
|
100
|
|
|
|
87
|
return $obj ? \1 : \0 if $o->{type} eq 'bool'; |
|
|
100
|
|
|
|
|
|
83
|
14
|
100
|
|
|
|
39
|
return "$obj" if $o->{type} eq 'scalar'; |
84
|
13
|
100
|
100
|
|
|
35
|
return [map $o->{values}->coerce_for_json($_, %opt), @$obj] if $o->{type} eq 'array' && $o->{values}; |
85
|
|
|
|
|
|
|
return {map { |
86
|
|
|
|
|
|
|
$o->{keys}{$_} ? ($_, $o->{keys}{$_}->coerce_for_json($obj->{$_}, %opt)) : |
87
|
|
|
|
|
|
|
$opt{unknown} eq 'pass' ? ($_, $obj->{$_}) : |
88
|
10
|
100
|
|
|
|
214
|
$opt{unknown} eq 'remove' ? () |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
89
|
|
|
|
|
|
|
: croak "Unknown key '$_' in hash in coerce_for_json()" |
90
|
12
|
100
|
100
|
|
|
49
|
} keys %$obj} if $o->{type} eq 'hash' && $o->{keys}; |
91
|
6
|
|
|
|
|
46
|
$obj |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# Returns a Cpanel::JSON::XS::Type; Behavior is subtly different compared to coerce_for_json(): |
96
|
|
|
|
|
|
|
# - Unknown keys in hashes will cause Cpanel::JSON::XS to die() |
97
|
|
|
|
|
|
|
# - Numbers are always formatted as floats (e.g. 10.0) even if it's a round nunmber |
98
|
|
|
|
|
|
|
sub json_type { |
99
|
0
|
|
|
0
|
0
|
0
|
my $o = shift; |
100
|
0
|
|
|
|
|
0
|
require Cpanel::JSON::XS::Type; |
101
|
0
|
0
|
|
|
|
0
|
return Cpanel::JSON::XS::Type::JSON_TYPE_FLOAT_OR_NULL() if $o->{type} eq 'num'; |
102
|
0
|
0
|
|
|
|
0
|
return Cpanel::JSON::XS::Type::JSON_TYPE_INT_OR_NULL() if $o->{type} eq 'int'; |
103
|
0
|
0
|
|
|
|
0
|
return Cpanel::JSON::XS::Type::JSON_TYPE_BOOL_OR_NULL() if $o->{type} eq 'bool'; |
104
|
0
|
0
|
|
|
|
0
|
return Cpanel::JSON::XS::Type::JSON_TYPE_STRING_OR_NULL() if $o->{type} eq 'scalar'; |
105
|
0
|
0
|
|
|
|
0
|
return Cpanel::JSON::XS::Type::json_type_null_or_anyof(Cpanel::JSON::XS::Type::json_type_arrayof($o->{values} ? $o->{values}->json_type : undef)) if $o->{type} eq 'array'; |
|
|
0
|
|
|
|
|
|
106
|
0
|
0
|
0
|
|
|
0
|
return Cpanel::JSON::XS::Type::json_type_null_or_anyof({ map +($_, $o->{keys}{$_}->json_type), keys %{$o->{keys}} }) if $o->{type} eq 'hash' && $o->{keys}; |
|
0
|
|
|
|
|
0
|
|
107
|
0
|
0
|
|
|
|
0
|
return Cpanel::JSON::XS::Type::json_type_null_or_anyof(Cpanel::JSON::XS::Type::json_type_hashof(undef)) if $o->{type} eq 'hash'; |
108
|
|
|
|
|
|
|
undef |
109
|
0
|
|
|
|
|
0
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
# Attempts to convert a stringified Perl regex into something that is compatible with JS. |
113
|
|
|
|
|
|
|
# - @ should not be escaped |
114
|
|
|
|
|
|
|
# - (?^: is a perl alias for (?d-imnsx: |
115
|
|
|
|
|
|
|
# - Javascript doesn't officially support embedded modifiers in the first place, so these are removed |
116
|
|
|
|
|
|
|
# Regexes compiled with any of /imsx will not work properly. |
117
|
|
|
|
|
|
|
sub _re_compat { |
118
|
17
|
|
|
17
|
|
564
|
local $_ = $_[0]; |
119
|
17
|
|
|
|
|
45
|
s/\\@/@/g; |
120
|
17
|
|
|
|
|
162
|
s{\(\?\^?[alupimnsx]*(?:-[imnsx]+)?(?=[:\)])}{(?}g; |
121
|
17
|
|
|
|
|
123
|
$_ |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub _join_regexes { |
126
|
9
|
|
|
9
|
|
13
|
my %r = map +($_,1), @{$_[0]}; |
|
9
|
|
|
|
|
47
|
|
127
|
9
|
|
|
|
|
31
|
my @r = sort keys %r; |
128
|
9
|
|
|
|
|
44
|
_re_compat join('', map "(?=$_)", @r[0..$#r-1]).$r[$#r] |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# Returns a few HTML5 validation properties. Doesn't include the 'type' |
133
|
|
|
|
|
|
|
sub html5_validation { |
134
|
15
|
|
|
15
|
0
|
23
|
my $o = shift; |
135
|
|
|
|
|
|
|
+( |
136
|
|
|
|
|
|
|
$o->{required} ? (required => 'required') : (), |
137
|
|
|
|
|
|
|
defined $o->{minlength} ? (minlength => $o->{minlength}) : (), |
138
|
|
|
|
|
|
|
defined $o->{maxlength} ? (maxlength => $o->{maxlength}) : (), |
139
|
|
|
|
|
|
|
defined $o->{min} ? (min => $o->{min} ) : (), |
140
|
|
|
|
|
|
|
defined $o->{max} ? (max => $o->{max} ) : (), |
141
|
15
|
100
|
|
|
|
112
|
$o->{regexes} ? (pattern => _join_regexes $o->{regexes}) : (), |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
142
|
|
|
|
|
|
|
); |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# The elm_ are experimental, unstable, not very well-tested and for Elm 0.19 |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# Options: required any array values keys indent level |
150
|
|
|
|
|
|
|
sub elm_type { |
151
|
0
|
|
|
0
|
0
|
|
my($o, %opt) = @_; |
152
|
0
|
0
|
|
0
|
|
|
my $par = delete $opt{_need_parens} ? sub { "($_[0])" } : sub { $_[0] }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
153
|
0
|
0
|
0
|
|
|
|
return $par->('Maybe ' . $o->elm_type(%opt, required => 1, _need_parens => 1)) if !$o->{required} && !defined $o->{default} && !$opt{required}; |
|
|
|
0
|
|
|
|
|
154
|
0
|
|
|
|
|
|
delete $opt{required}; |
155
|
0
|
0
|
|
|
|
|
return 'String' if $o->{type} eq 'scalar'; |
156
|
0
|
0
|
|
|
|
|
return 'Bool' if $o->{type} eq 'bool'; |
157
|
0
|
0
|
|
|
|
|
return 'Float' if $o->{type} eq 'num'; |
158
|
0
|
0
|
|
|
|
|
return 'Int' if $o->{type} eq 'int'; |
159
|
0
|
0
|
0
|
|
|
|
return $opt{any} if $o->{type} eq 'any' && $opt{any}; |
160
|
|
|
|
|
|
|
return $par->( ($opt{array} || 'List') . ' ' . ($opt{values} || $o->{values}->elm_type(%opt, _need_parens => 1)) ) |
161
|
0
|
0
|
0
|
|
|
|
if $o->{type} eq 'array' && ($opt{values} || $o->{values}); |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
162
|
|
|
|
|
|
|
|
163
|
0
|
0
|
0
|
|
|
|
if($o->{type} eq 'hash' && ($o->{keys} || $opt{keys})) { |
|
|
|
0
|
|
|
|
|
164
|
0
|
|
0
|
|
|
|
$opt{indent} //= 2; |
165
|
0
|
|
0
|
|
|
|
$opt{level} //= 1; |
166
|
0
|
|
|
|
|
|
my $len = 0; |
167
|
0
|
0
|
|
|
|
|
$len = length $_ > $len ? length $_ : $len for keys %{$o->{keys}}; |
|
0
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
my $r = "\n{ " . join("\n, ", map { |
170
|
0
|
|
0
|
|
|
|
sprintf "%-*s : %s", $len, $_, $opt{keys}{$_} || $o->{keys}{$_}->elm_type(%opt, level => $opt{level}+1); |
171
|
0
|
|
|
|
|
|
} sort keys %{$o->{keys}}) . "\n}";; |
|
0
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
|
173
|
0
|
0
|
|
|
|
|
$r =~ s/\n/$opt{indent} ? "\n" . (' 'x($opt{indent}*$opt{level})) : ''/eg; |
|
0
|
|
|
|
|
|
|
174
|
0
|
|
|
|
|
|
return $r; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
0
|
|
|
|
|
|
croak "Unknown type '$o->{type}' or missing option"; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
# Elm JSON encoder for values of elm_type() |
182
|
|
|
|
|
|
|
# options: elm_type() options + json_encode var_prefix |
183
|
|
|
|
|
|
|
sub elm_encoder { |
184
|
0
|
|
|
0
|
0
|
|
my($o, %opt) = @_; |
185
|
0
|
|
0
|
|
|
|
$opt{json_encode} //= ''; |
186
|
0
|
|
0
|
|
|
|
$opt{var_prefix} //= 'e'; |
187
|
0
|
|
0
|
|
|
|
$opt{var_num} //= 0; |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
return sprintf '(Maybe.withDefault %snull << Maybe.map %s)', |
190
|
|
|
|
|
|
|
$opt{json_encode}, $opt{values} || $o->elm_encoder(%opt, required => 1) |
191
|
0
|
0
|
0
|
|
|
|
if !$o->{required} && !defined $o->{default} && !$opt{required}; |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
192
|
|
|
|
|
|
|
|
193
|
0
|
|
|
|
|
|
delete $opt{required}; |
194
|
0
|
0
|
|
|
|
|
return "$opt{json_encode}string" if $o->{type} eq 'scalar'; |
195
|
0
|
0
|
|
|
|
|
return "$opt{json_encode}bool" if $o->{type} eq 'bool'; |
196
|
0
|
0
|
|
|
|
|
return "$opt{json_encode}float" if $o->{type} eq 'num'; |
197
|
0
|
0
|
|
|
|
|
return "$opt{json_encode}int" if $o->{type} eq 'int'; |
198
|
0
|
0
|
0
|
|
|
|
return $opt{any} if $o->{type} eq 'any' && $opt{any}; |
199
|
|
|
|
|
|
|
return sprintf '(%slist %s)', $opt{json_encode}, $opt{values} || $o->{values}->elm_encoder(%opt) |
200
|
0
|
0
|
0
|
|
|
|
if $o->{type} eq 'array' && ($opt{values} || $o->{values}); |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
201
|
|
|
|
|
|
|
|
202
|
0
|
0
|
0
|
|
|
|
if($o->{type} eq 'hash' && ($o->{keys} || $opt{keys})) { |
|
|
|
0
|
|
|
|
|
203
|
0
|
|
0
|
|
|
|
$opt{indent} //= 2; |
204
|
0
|
|
0
|
|
|
|
$opt{level} //= 1; |
205
|
0
|
|
|
|
|
|
my $len = 0; |
206
|
0
|
0
|
|
|
|
|
$len = length $_ > $len ? length $_ : $len for keys %{$o->{keys}}; |
|
0
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
|
208
|
0
|
|
|
|
|
|
my $var = $opt{var_prefix}.$opt{var_num}; |
209
|
|
|
|
|
|
|
my $r = sprintf "(\\%s -> %sobject\n[ %s\n])", $var, $opt{json_encode}, join "\n, ", map { |
210
|
|
|
|
|
|
|
sprintf '("%s",%s %s %s.%1$s)', $_, |
211
|
|
|
|
|
|
|
' 'x($len-(length $_)), |
212
|
0
|
|
0
|
|
|
|
$opt{keys}{$_} || $o->{keys}{$_}->elm_encoder(%opt, level => $opt{level}+1, var_num => $opt{var_num}+1), |
213
|
|
|
|
|
|
|
$var; |
214
|
0
|
|
|
|
|
|
} sort keys %{$o->{keys}}; |
|
0
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
|
216
|
0
|
0
|
|
|
|
|
$r =~ s/\n/$opt{indent} ? "\n" . (' 'x($opt{indent}*$opt{level})) : ''/eg; |
|
0
|
|
|
|
|
|
|
217
|
0
|
|
|
|
|
|
return $r; |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
|
220
|
0
|
|
|
|
|
|
croak "Unknown type '$o->{type}' or missing option"; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# Elm JSON decoder for values of elm_type() |
225
|
|
|
|
|
|
|
# options: elm_type() options + json_decode var_prefix |
226
|
|
|
|
|
|
|
sub elm_decoder { |
227
|
0
|
|
|
0
|
0
|
|
my($o, %opt) = @_; |
228
|
0
|
|
0
|
|
|
|
$opt{json_decode} //= ''; |
229
|
0
|
|
0
|
|
|
|
$opt{var_prefix} //= 'd'; |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
return sprintf '(%snullable %s)', |
232
|
|
|
|
|
|
|
$opt{json_decode}, $opt{values} || $o->elm_decoder(%opt, required => 1) |
233
|
0
|
0
|
0
|
|
|
|
if !$o->{required} && !defined $o->{default} && !$opt{required}; |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
234
|
|
|
|
|
|
|
|
235
|
0
|
|
|
|
|
|
delete $opt{required}; |
236
|
0
|
0
|
|
|
|
|
return "$opt{json_decode}string" if $o->{type} eq 'scalar'; |
237
|
0
|
0
|
|
|
|
|
return "$opt{json_decode}bool" if $o->{type} eq 'bool'; |
238
|
0
|
0
|
|
|
|
|
return "$opt{json_decode}float" if $o->{type} eq 'num'; |
239
|
0
|
0
|
|
|
|
|
return "$opt{json_decode}int" if $o->{type} eq 'int'; |
240
|
0
|
0
|
0
|
|
|
|
return $opt{any} if $o->{type} eq 'any' && $opt{any}; |
241
|
0
|
0
|
|
|
|
|
return "$opt{json_decode}value" if $o->{type} eq 'any'; |
242
|
|
|
|
|
|
|
return sprintf '(%slist %s)', $opt{json_decode}, $opt{values} || $o->{values}->elm_decoder(%opt) |
243
|
0
|
0
|
0
|
|
|
|
if $o->{type} eq 'array' && ($opt{values} || $o->{values}); |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
244
|
|
|
|
|
|
|
|
245
|
0
|
0
|
0
|
|
|
|
if($o->{type} eq 'hash' && ($o->{keys} || $opt{keys})) { |
|
|
|
0
|
|
|
|
|
246
|
0
|
|
0
|
|
|
|
$opt{indent} //= 2; |
247
|
0
|
|
0
|
|
|
|
$opt{level} //= 1; |
248
|
0
|
|
|
|
|
|
my $len = 0; |
249
|
0
|
0
|
|
|
|
|
$len = length $_ > $len ? length $_ : $len for keys %{$o->{keys}}; |
|
0
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
|
251
|
0
|
|
|
|
|
|
my $r; |
252
|
0
|
|
|
|
|
|
my $num = keys %{$o->{keys}}; |
|
0
|
|
|
|
|
|
|
253
|
0
|
|
|
|
|
|
my $varnum = 1; |
254
|
0
|
|
|
0
|
|
|
my $getvar = sub { $opt{var_prefix}.($varnum++) }; |
|
0
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# For 8 members or less we can use the simple Json.Decode.map* functions. |
257
|
0
|
0
|
|
|
|
|
if($num <= 8) { |
258
|
0
|
|
|
|
|
|
my(@fnarg, @assign, @fetch); |
259
|
0
|
|
|
|
|
|
for (sort keys %{$o->{keys}}) { |
|
0
|
|
|
|
|
|
|
260
|
0
|
|
|
|
|
|
my $var = $getvar->(); |
261
|
0
|
|
|
|
|
|
push @fnarg, $var; |
262
|
0
|
|
|
|
|
|
push @assign, "$_ = $var"; |
263
|
|
|
|
|
|
|
push @fetch, sprintf '(%sfield "%s"%s %s)', $opt{json_decode}, $_, |
264
|
|
|
|
|
|
|
' 'x($len-(length $_)), |
265
|
0
|
|
0
|
|
|
|
$opt{keys}{$_} || $o->{keys}{$_}->elm_decoder(%opt, var_prefix => $var, level => $opt{level}+1); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
$r = sprintf "(%smap%s\n(\\%s -> { %s })\n%s)", |
268
|
0
|
0
|
|
|
|
|
$opt{json_decode}, $num == 1 ? '' : $num, join(' ', @fnarg), join(', ', @assign), join("\n", @fetch); |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
# For larger hashes we go through Json.Decode.dict and a little custom decoding logic. |
271
|
|
|
|
|
|
|
# Json.Decode only allows failing with an error string, so the error messages aren't as good. |
272
|
|
|
|
|
|
|
} else { |
273
|
0
|
|
|
|
|
|
my($dict, $fn, $name, $dec, $next, $cap) = map $getvar->(), 1..6; |
274
|
0
|
|
|
|
|
|
my(@assign, @fn); |
275
|
0
|
|
|
|
|
|
for (sort keys %{$o->{keys}}) { |
|
0
|
|
|
|
|
|
|
276
|
0
|
|
|
|
|
|
my $var = $getvar->(); |
277
|
0
|
|
|
|
|
|
push @assign, "$_ = $var"; |
278
|
|
|
|
|
|
|
push @fn, sprintf '%s "%s"%s %s (\%s ->', $fn, $_, |
279
|
|
|
|
|
|
|
' 'x($len-(length $_)), |
280
|
0
|
|
0
|
|
|
|
$opt{keys}{$_} || $o->{keys}{$_}->elm_decoder(%opt, var_prefix => "${var}_", level => $opt{level}+1), |
281
|
|
|
|
|
|
|
$var; |
282
|
|
|
|
|
|
|
} |
283
|
0
|
|
|
|
|
|
my $spc = ' 'x(12 + length($fn) + length($name) + length($dec) + length($next)); |
284
|
0
|
|
|
|
|
|
$r = "($opt{json_decode}andThen (\\$dict -> \n" |
285
|
|
|
|
|
|
|
."let $fn $name $dec $next = case Maybe.map ($opt{json_decode}decodeValue $dec) (Dict.get $name $dict) of\n" |
286
|
|
|
|
|
|
|
."${spc}Nothing -> $opt{json_decode}fail (\"Missing key '\"++$name++\"'\")\n" |
287
|
|
|
|
|
|
|
."${spc}Just (Err $cap) -> $opt{json_decode}fail (\"Error decoding value of '\"++$name++\"': \"++($opt{json_decode}errorToString $cap))\n" |
288
|
|
|
|
|
|
|
."${spc}Just (Ok $cap) -> $next $cap\n" |
289
|
|
|
|
|
|
|
."in ".join("\n ", @fn)."\n" |
290
|
|
|
|
|
|
|
." $opt{json_decode}succeed { ".join(', ', @assign)." }\n" |
291
|
|
|
|
|
|
|
.')'.(')'x@fn)." ($opt{json_decode}dict $opt{json_decode}value))"; |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
0
|
0
|
|
|
|
|
$r =~ s/\n/$opt{indent} ? "\n" . (' 'x($opt{indent}*$opt{level})) : ''/eg; |
|
0
|
|
|
|
|
|
|
295
|
0
|
|
|
|
|
|
return $r; |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
0
|
|
|
|
|
|
croak "Unknown type '$o->{type}' or missing option"; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
1; |