File Coverage

blib/lib/Data/Sah/Compiler/perl/TH/hash.pm
Criterion Covered Total %
statement 187 194 96.3
branch 57 74 77.0
condition 16 16 100.0
subroutine 25 25 100.0
pod 0 18 0.0
total 285 327 87.1


line stmt bran cond sub pod time code
1              
2             use 5.010;
3 2     2   40 use strict;
  2         8  
4 2     2   9 use warnings;
  2         4  
  2         47  
5 2     2   9 #use Log::Any '$log';
  2         4  
  2         54  
6              
7             use Data::Dmp;
8 2     2   8  
  2         3  
  2         108  
9             use Mo qw(build default);
10 2     2   12 use Role::Tiny::With;
  2         5  
  2         12  
11 2     2   486  
  2         5  
  2         5034  
12             extends 'Data::Sah::Compiler::perl::TH';
13             with 'Data::Sah::Type::hash';
14              
15             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
16             our $DATE = '2022-10-19'; # DATE
17             our $DIST = 'Data-Sah'; # DIST
18             our $VERSION = '0.914'; # VERSION
19              
20             my ($self, $cd) = @_;
21             my $c = $self->compiler;
22 742     742 0 2111  
23 742         3251 my $dt = $cd->{data_term};
24             $cd->{_ccl_check_type} = "ref($dt) eq 'HASH'";
25 742         4442 }
26 742         3589  
27             my ($self, $which, $cd) = @_;
28             my $c = $self->compiler;
29             my $ct = $cd->{cl_term};
30 288     288 0 767 my $dt = $cd->{data_term};
31 288         1050  
32 288         1498 $c->add_runtime_module($cd, $cd->{args}{dump_module});
33 288         768  
34             if ($which eq 'is') {
35 288         1459 $c->add_ccl($cd, $c->expr_dump($cd, $dt).' eq '.$c->expr_dump($cd, $ct));
36             } elsif ($which eq 'in') {
37 288 100       1645 $c->add_ccl($cd, "do { my \$_sahv_dt_str = ".$c->expr_dump($cd, $dt)."; my \$_sahv_res = 0; " .
    50          
38 150         892 "for my \$_sahv_el (\@{ $ct }) { my \$_sahv_el_str = ".$c->expr_dump($cd, "\$_sahv_el")."; ".
39             "if (\$_sahv_dt_str eq \$_sahv_el_str) { \$_sahv_res = 1; last } } \$_sahv_res }");
40 138         481 }
41             }
42              
43             my ($self_th, $which, $cd) = @_;
44             my $c = $self_th->compiler;
45             my $cv = $cd->{cl_value};
46             my $ct = $cd->{cl_term};
47 115     115 0 369 my $dt = $cd->{data_term};
48 115         475  
49 115         631 if ($which eq 'len') {
50 115         340 $c->add_ccl($cd, "keys(\%{$dt}) == $ct");
51 115         343 } elsif ($which eq 'min_len') {
52             $c->add_ccl($cd, "keys(\%{$dt}) >= $ct");
53 115 100       959 } elsif ($which eq 'max_len') {
    100          
    100          
    100          
    100          
    100          
    50          
    0          
    0          
    0          
    0          
54 6         31 $c->add_ccl($cd, "keys(\%{$dt}) <= $ct");
55             } elsif ($which eq 'len_between') {
56 34         175 if ($cd->{cl_is_expr}) {
57             $c->add_ccl(
58 21         118 $cd, "keys(\%{$dt}) >= $ct\->[0] && ".
59             "keys(\%{$dt}) >= $ct\->[1]");
60 6 50       23 } else {
61 0         0 # simplify code
62             $c->add_ccl(
63             $cd, "keys(\%{$dt}) >= $cv->[0] && ".
64             "keys(\%{$dt}) <= $cv->[1]");
65             }
66 6         84 } elsif ($which eq 'has') {
67             $c->add_runtime_module($cd, $cd->{args}{dump_module});
68             $c->add_ccl($cd, "do { my \$_sahv_ct_str = ".$c->expr_dump($cd, $ct)."; my \$_sahv_res = 0; " .
69             "for my \$_sahv_el (values \%{ $dt }) { my \$_sahv_el_str = ".$c->expr_dump($cd, "\$_sahv_el")."; ".
70             "if (\$_sahv_ct_str eq \$_sahv_el_str) { \$_sahv_res = 1; last } } \$_sahv_res }");
71 18         85 } elsif ($which eq 'each_index') {
72 18         76 $self_th->set_tmp_data_term($cd) if $cd->{args}{data_term_includes_topic_var};
73             $self_th->gen_each($cd, "sort keys(\%{$cd->{data_term}})", '', '$_');
74             $self_th->restore_data_term($cd) if $cd->{args}{data_term_includes_topic_var};
75             } elsif ($which eq 'each_elem') {
76 12 50       53 $self_th->set_tmp_data_term($cd) if $cd->{args}{data_term_includes_topic_var};
77 12         107 $self_th->gen_each($cd, "sort keys(\%{$cd->{data_term}})", '_', "$cd->{data_term}\->{\$_}");
78 12 50       97 $self_th->restore_data_term($cd) if $cd->{args}{data_term_includes_topic_var};
79             } elsif ($which eq 'check_each_index') {
80 18 50       66 $self_th->compiler->_die_unimplemented_clause($cd);
81 18         152 } elsif ($which eq 'check_each_elem') {
82 18 50       113 $self_th->compiler->_die_unimplemented_clause($cd);
83             } elsif ($which eq 'uniq') {
84 0         0 $self_th->compiler->_die_unimplemented_clause($cd);
85             } elsif ($which eq 'exists') {
86 0         0 $self_th->compiler->_die_unimplemented_clause($cd);
87             }
88 0         0 }
89              
90 0         0 my ($self_th, $which, $cd) = @_;
91             my $c = $self_th->compiler;
92             my $cv = $cd->{cl_value};
93             my $dt = $cd->{data_term};
94              
95 55     55   184 local $cd->{_subdata_level} = $cd->{_subdata_level} + 1;
96 55         207  
97 55         326 # we handle subdata manually here, because in generated code for
98 55         173 # {keys,re_keys}.restrict, we haven't delved into the keys
99              
100 55         234 my $jccl;
101             {
102             local $cd->{ccls} = [];
103              
104             my $lit_valid_keys;
105 55         113 if ($which eq 'keys') {
106             $lit_valid_keys = $c->literal([sort keys %$cv]);
107 55         88 } else {
  55         157  
108             $lit_valid_keys = "[".
109 55         93 join(",", map { "qr/".$c->_str2reliteral($cd, $_)."/" }
110 55 100       226 sort keys %$cv)."]";
111 46         252 }
112              
113             $self_th->set_tmp_data_term($cd) if $cd->{args}{data_term_includes_topic_var};
114 9         34  
  9         47  
115             if ($cd->{clset}{"$which.restrict"} // 1) {
116             local $cd->{_debug_ccl_note} = "$which.restrict";
117             #$c->add_runtime_module($cd, "List::Util");
118 55 100       3786 $c->add_ccl(
119             $cd,
120 55 100 100     424 #"!defined(List::Util::first(sub { my \$ditem=\$_; !defined(List::Util::first(sub {\$ditem ".($which eq 'keys' ? 'eq' : '=~')." \$_ }, \@{ $lit_valid_keys })) }, keys %{ $dt }))",
121 52         199 "!(grep { my \$_sahv_dt_item=\$_; !(grep { \$_sahv_dt_item ".($which eq 'keys' ? 'eq' : '=~')." \$_ } \@{ $lit_valid_keys }) } keys %{ $dt })",
122             {
123 52 100       444 err_msg => 'TMP',
    100          
124             err_expr => join(
125             "",
126             'sprintf(',
127             $c->literal($c->_xlt(
128             $cd, "hash contains ".
129             "unknown field(s) (%s)")),
130             ',',
131             "join(', ', sort grep { my \$_sahv_dt_item=\$_; !(grep { \$_sahv_dt_item ".($which eq 'keys' ? 'eq':'=~')." \$_ } \@{ $lit_valid_keys })} keys %{ $dt })",
132             ')',
133             ),
134             },
135             );
136             $self_th->restore_data_term($cd) if $cd->{args}{data_term_includes_topic_var};
137             }
138             delete $cd->{uclset}{"$which.restrict"};
139              
140             my $cdef;
141 52 100       291 if ($which eq 'keys') {
142             $cdef = $cd->{clset}{"keys.create_default"} // 1;
143 55         175 delete $cd->{uclset}{"keys.create_default"};
144             }
145 55         87  
146 55 100       162 $self_th->set_tmp_data_term($cd) if $cd->{args}{data_term_includes_topic_var};
147 46   100     212  
148 46         106 my $nkeys = scalar(keys %$cv);
149             my $i = 0;
150             for my $k (sort keys %$cv) {
151 55 100       231 my $kre = $c->_str2reliteral($cd, $k);
152             local $cd->{spath} = [@{ $cd->{spath} }, $k];
153 55         151 ++$i;
154 55         108 my $nsch = $c->main->normalize_schema($cv->{$k});
155 55         218 my $kdn = $k; $kdn =~ s/\W+/_/g;
156 100         368 my $klit = $which eq 're_keys' ? '$_' : $c->literal($k);
157 100         4635 my $kdt = "$dt\->{$klit}";
  100         412  
158 100         181 my %iargs = %{$cd->{args}};
159 100         464 $iargs{outer_cd} = $cd;
160 100         3020 $iargs{data_name} = $kdn;
  100         305  
161 100 100       401 $iargs{data_term} = $kdt;
162 100         2707 $iargs{schema} = $nsch;
163 100         189 $iargs{schema_is_normalized} = 1;
  100         1381  
164 100         307 $iargs{cache} = $cd->{args}{cache};
165 100         305 $iargs{indent_level}++;
166 100         196 $iargs{data_term_includes_topic_var} = 1 if $which eq 're_keys';
167 100         251 my $icd = $c->compile(%iargs);
168 100         231  
169 100         271 # should we set default for hash value?
170 100         198 my $sdef = $cdef && defined($nsch->[1]{default});
171 100 100       305  
172 100         575 # stack is used to store (non-bool) subresults
173             $c->add_var($cd, '_sahv_stack', []) if $cd->{use_dpath};
174              
175 100   100     674 $c->add_runtime_module($cd, "List::Util") if $which eq 're_keys'; # for re_keys
176              
177             my @code = (
178 100 100       545 ($c->indent_str($cd), "(push(@\$_sahv_dpath, undef), push(\@\$_sahv_stack, undef), \$_sahv_stack->[-1] = \n")
179             x !!($cd->{use_dpath} && $i == 1),
180 100 100       335  
181             # for re_keys, we iterate over all data's keys which match regex
182             ('(!defined(List::Util::first(sub {!(')
183             x !!($which eq 're_keys'),
184              
185             $which eq 're_keys' ? "\$_ !~ /$kre/ || (" :
186             ($sdef ? "" : "!exists($kdt) || ("),
187              
188             ($c->indent_str($cd), "(\$_sahv_dpath->[-1] = ".
189             ($which eq 're_keys' ? '$_' : $klit)."),\n")
190             x !!$cd->{use_dpath},
191             $icd->{result}, "\n",
192              
193             $which eq 're_keys' || !$sdef ? ")" : "",
194              
195             # close iteration over all data's keys which match regex
196             (")}, sort keys %{ $dt })))")
197             x !!($which eq 're_keys'),
198              
199             ($c->indent_str($cd), "), pop(\@\$_sahv_dpath), pop(\@\$_sahv_stack)\n")
200             x !!($cd->{use_dpath} && $i == $nkeys),
201             );
202             my $ires = join("", @code);
203             local $cd->{_debug_ccl_note} = "$which: ".$c->literal($k);
204             $c->add_ccl($cd, $ires);
205 100 100 100     447 }
    100 100        
    100 100        
    100          
206              
207 100         1248 $self_th->restore_data_term($cd) if $cd->{args}{data_term_includes_topic_var};
208 100         351  
209 100         3262 $jccl = $c->join_ccls(
210             $cd, $cd->{ccls}, {err_msg => ''});
211             }
212 55 100       305 $c->add_ccl($cd, $jccl, {});
213             }
214              
215 55         333 my ($self, $cd) = @_;
216             $self->_clause_keys_or_re_keys('keys', $cd);
217 55         279 }
218              
219             my ($self, $cd) = @_;
220             $self->_clause_keys_or_re_keys('re_keys', $cd);
221 46     46 0 128 }
222 46         274  
223             my ($self, $cd) = @_;
224             my $c = $self->compiler;
225             my $ct = $cd->{cl_term};
226 9     9 0 38 my $dt = $cd->{data_term};
227 9         58  
228             # we assign to $_sahv_h first to avoid variable clashing if $dt is '$_'.
229              
230             $c->add_runtime_module($cd, "List::Util");
231 45     45 0 120 $c->add_ccl(
232 45         167 $cd,
233 45         255 "do { my \$_sahv_h = $dt; !defined(List::Util::first(sub {!exists(\$_sahv_h\->{\$_})}, \@{ $ct })) }",
234 45         110 {
235             err_msg => 'TMP',
236             err_expr =>
237             "sprintf(".
238 45         157 $c->literal($c->_xlt($cd, "hash has missing required field(s) (%s)")).
239 45         360 ",join(', ', do { my \$_sahv_h = $dt; grep { !exists(\$_sahv_h\->{\$_}) } \@{ $ct } }))"
240             }
241             );
242             }
243              
244             my ($self, $cd) = @_;
245             my $c = $self->compiler;
246             my $ct = $cd->{cl_term};
247             my $dt = $cd->{data_term};
248              
249             #$c->add_runtime_module($cd, "List::Util");
250             $c->add_ccl(
251             $cd,
252             #"!defined(List::Util::first(sub { my \$_sahv_dt_item=\$_; !defined(List::Util::first!(sub { \$_sahv_dt_item eq \$_ }, \@{ $ct })) }, keys \%{ $dt }))",
253 9     9 0 28 "!(grep { my \$_sahv_dt_item=\$_; !(grep { \$_sahv_dt_item eq \$_ } \@{ $ct }) } keys \%{ $dt })",
254 9         27 {
255 9         48 err_msg => 'TMP',
256 9         18 err_expr =>
257             "sprintf(".
258             $c->literal($c->_xlt($cd, "hash contains non-allowed field(s) (%s)")).
259 9         44 ",join(', ', sort grep { my \$_sahv_dt_item=\$_; !(grep { \$_sahv_dt_item eq \$_ } \@{ $ct }) } keys \%{ $dt }))"
260             }
261             );
262             }
263              
264             my ($self, $cd) = @_;
265             my $c = $self->compiler;
266             #my $ct = $cd->{cl_term};
267             my $cv = $cd->{cl_value};
268             my $dt = $cd->{data_term};
269              
270             if ($cd->{cl_is_expr}) {
271             # i'm lazy atm and does not need expr yet
272             $c->_die_unimplemented_clause($cd, "with expr");
273             }
274 9     9 0 20  
275 9         30 my $re = $c->_str2reliteral($cd, $cv);
276             #$c->add_runtime_module($cd, "List::Util");
277 9         43 $c->add_ccl(
278 9         19 $cd,
279             #"!defined(List::Util::first(sub {\$_ !~ /$re/}, keys \%{ $dt }))",
280 9 50       25 "!(grep {\$_ !~ /$re/} keys \%{ $dt })",
281             {
282 0         0 err_msg => 'TMP',
283             err_expr =>
284             "sprintf(".
285 9         24 $c->literal($c->_xlt($cd, "hash contains non-allowed field(s) (%s)")).
286             ",join(', ', sort grep { \$_ !~ /$re/ } keys \%{ $dt }))"
287 9         470 }
288             );
289             }
290              
291             my ($self, $cd) = @_;
292             my $c = $self->compiler;
293             my $ct = $cd->{cl_term};
294             my $dt = $cd->{data_term};
295              
296             #$c->add_runtime_module($cd, "List::Util");
297             $c->add_ccl(
298             $cd,
299             #"!defined(List::Util::first(sub {\$_ ~~ $ct}, keys \%{ $dt }))",
300             "!(grep { my \$_sahv_dt_item=\$_; !!(grep { \$_sahv_dt_item eq \$_ } \@{ $ct }) } keys \%{ $dt })",
301             {
302 9     9 0 25 err_msg => 'TMP',
303 9         29 err_expr =>
304 9         42 "sprintf(".
305 9         17 $c->literal($c->_xlt($cd, "hash contains forbidden field(s) (%s)")).
306             ",join(', ', sort grep { my \$_sahv_dt_item=\$_; !(grep { \$_sahv_dt_item eq \$_ } \@{ $ct }) } keys \%{ $dt }))"
307             }
308 9         40 );
309             }
310              
311             my ($self, $cd) = @_;
312             my $c = $self->compiler;
313             #my $ct = $cd->{cl_term};
314             my $cv = $cd->{cl_value};
315             my $dt = $cd->{data_term};
316              
317             if ($cd->{cl_is_expr}) {
318             # i'm lazy atm and does not need expr yet
319             $c->_die_unimplemented_clause($cd, "with expr");
320             }
321              
322             my $re = $c->_str2reliteral($cd, $cv);
323 9     9 0 24 #$c->add_runtime_module($cd, "List::Util");
324 9         28 $c->add_ccl(
325             $cd,
326 9         40 #"!defined(List::Util::first(sub {\$_ =~ /$re/}, keys \%{ $dt }))",
327 9         21 "!(grep {\$_ =~ /$re/} keys \%{ $dt })",
328             {
329 9 50       27 err_msg => 'TMP',
330             err_expr =>
331 0         0 "sprintf(".
332             $c->literal($c->_xlt($cd, "hash contains forbidden field(s) (%s)")).
333             ",join(', ', sort grep { \$_ =~ /$re/ } keys \%{ $dt }))"
334 9         32 }
335             );
336 9         434 }
337              
338             my ($self, $cd) = @_;
339             my $c = $self->compiler;
340             my $ct = $cd->{cl_term};
341             my $dt = $cd->{data_term};
342              
343             # we assign to $_sahv_h first to avoid variable clashing if $dt is '$_'.
344              
345             $c->add_runtime_module($cd, "List::Util");
346             $c->add_ccl(
347             $cd,
348             "do { my \$_sahv_h = $dt; List::Util::sum(map {exists(\$_sahv_h\->{\$_}) ? 1:0} \@{ $ct }) <= 1 }",
349             {},
350             );
351 36     36 0 85 }
352 36         107  
353 36         199 my ($self, $cd) = @_;
354 36         83 my $c = $self->compiler;
355             my $ct = $cd->{cl_term};
356             my $dt = $cd->{data_term};
357              
358 36         112 # we assign to $h first to avoid variable clashing if $dt is '$_'.
359 36         210  
360             $c->add_runtime_module($cd, "List::Util");
361             $c->add_ccl(
362             $cd,
363             "do { my \$_sahv_h = $dt; my \$_sahv_keys = $ct; my \$_sahv_tot = List::Util::sum(map {exists(\$_sahv_h\->{\$_}) ? 1:0} \@\$_sahv_keys); \$_sahv_tot==0 || \$_sahv_tot==\@\$_sahv_keys }",
364             {},
365             );
366             }
367 36     36 0 114  
368 36         148 my ($self, $cd) = @_;
369 36         192 my $c = $self->compiler;
370 36         88 my $ct = $cd->{cl_term};
371             my $dt = $cd->{data_term};
372              
373             # we assign to $_sahv_h first to avoid variable clashing if $dt is '$_'.
374 36         119  
375 36         279 $c->add_runtime_module($cd, "List::Util");
376             $c->add_ccl(
377             $cd,
378             "do { my \$_sahv_h = $dt; List::Util::sum(map {exists(\$_sahv_h\->{\$_}) ? 1:0} \@{ $ct }) == 1 }",
379             {},
380             );
381             }
382              
383 24     24 0 66 my ($self, $cd) = @_;
384 24         78 my $c = $self->compiler;
385 24         126 my $cv = $cd->{cl_value};
386 24         53 my $dt = $cd->{data_term};
387              
388             # we assign to $_sahv_h first to avoid variable clashing if $dt is '$_'.
389              
390 24         85 $c->add_runtime_module($cd, "List::Util");
391 24         147 $c->add_ccl(
392             $cd,
393             "do { my \$_sahv_h = $dt; my \$_sahv_n = List::Util::sum(map {exists(\$_sahv_h\->{\$_}) ? 1:0} \@{ ".$c->literal($cv->[2])." }); \$_sahv_n >= $cv->[0] && \$_sahv_n <= $cv->[1] }",
394             {},
395             );
396             }
397              
398             my ($self, $cd) = @_;
399 60     60 0 167 my $c = $self->compiler;
400 60         192 my $ct = $cd->{cl_term};
401 60         341 my $dt = $cd->{data_term};
402 60         142  
403             # we assign to $_sahv_h first to avoid variable clashing if $dt is '$_'.
404              
405             $c->add_runtime_module($cd, "List::Util");
406 60         201 $c->add_ccl(
407 60         338 $cd,
408             "do { my \$_sahv_h = $dt; my \$_sahv_ct = $ct; ".
409             "my \$_sahv_has_prereq = List::Util::sum(map {exists(\$_sahv_h\->{\$_}) ? 1:0} \@{ \$_sahv_ct->[1] }) ? 1:0; ".
410             "my \$_sahv_has_dep = List::Util::sum(map {exists(\$_sahv_h\->{\$_}) ? 1:0} (ref(\$_sahv_ct->[0]) eq 'ARRAY' ? \@{ \$_sahv_ct->[0] } : (\$_sahv_ct->[0]))) ? 1:0; ".
411             "!\$_sahv_has_dep || \$_sahv_has_prereq }",
412             {},
413             );
414             }
415 18     18 0 57  
416 18         79 my ($self, $cd) = @_;
417 18         107 my $c = $self->compiler;
418 18         47 my $ct = $cd->{cl_term};
419             my $dt = $cd->{data_term};
420              
421             # we assign to $_sahv_h first to avoid variable clashing if $dt is '$_'.
422 18         74  
423 18         173 $c->add_runtime_module($cd, "List::Util");
424             $c->add_ccl(
425             $cd,
426             "do { my \$_sahv_h = $dt; my \$_sahv_ct = $ct; ".
427             "my \$_sahv_has_prereq = List::Util::sum(map {exists(\$_sahv_h\->{\$_}) ? 1:0} \@{ \$_sahv_ct->[1] }) == \@{ \$_sahv_ct->[1] } ? 1:0; ".
428             "my \$_sahv_has_dep = List::Util::sum(map {exists(\$_sahv_h\->{\$_}) ? 1:0} (ref(\$_sahv_ct->[0]) eq 'ARRAY' ? \@{ \$_sahv_ct->[0] } : (\$_sahv_ct->[0]))) ? 1:0; ".
429             "!\$_sahv_has_dep || \$_sahv_has_prereq }",
430             {},
431             );
432             }
433              
434 18     18 0 53 my ($self, $cd) = @_;
435 18         75 my $c = $self->compiler;
436 18         92 my $ct = $cd->{cl_term};
437 18         56 my $dt = $cd->{data_term};
438              
439             # we assign to $_sahv_h first to avoid variable clashing if $dt is '$_'.
440              
441 18         65 $c->add_runtime_module($cd, "List::Util");
442 18         156 $c->add_ccl(
443             $cd,
444             "do { my \$_sahv_h = $dt; my \$_sahv_ct = $ct; ".
445             "my \$_sahv_has_prereq = List::Util::sum(map {exists(\$_sahv_h\->{\$_}) ? 1:0} \@{ \$_sahv_ct->[1] }) ? 1:0; ".
446             "my \$_sahv_has_dep = List::Util::sum(map {exists(\$_sahv_h\->{\$_}) ? 1:0} (ref(\$_sahv_ct->[0]) eq 'ARRAY' ? \@{ \$_sahv_ct->[0] } : (\$_sahv_ct->[0]))) ? 1:0; ".
447             "\$_sahv_has_dep || !\$_sahv_has_prereq }",
448             {},
449             );
450             }
451              
452             my ($self, $cd) = @_;
453 24     24 0 48 my $c = $self->compiler;
454 24         67 my $ct = $cd->{cl_term};
455 24         117 my $dt = $cd->{data_term};
456 24         52  
457             # we assign to $_sahv_h first to avoid variable clashing if $dt is '$_'.
458              
459             $c->add_runtime_module($cd, "List::Util");
460 24         65 $c->add_ccl(
461 24         155 $cd,
462             "do { my \$_sahv_h = $dt; my \$_sahv_ct = $ct; ".
463             "my \$_sahv_has_prereq = List::Util::sum(map {exists(\$_sahv_h\->{\$_}) ? 1:0} \@{ \$_sahv_ct->[1] }) == \@{ \$_sahv_ct->[1] } ? 1:0; ".
464             "my \$_sahv_has_dep = List::Util::sum(map {exists(\$_sahv_h\->{\$_}) ? 1:0} (ref(\$_sahv_ct->[0]) eq 'ARRAY' ? \@{ \$_sahv_ct->[0] } : (\$_sahv_ct->[0]))) ? 1:0; ".
465             "\$_sahv_has_dep || !\$_sahv_has_prereq }",
466             {},
467             );
468             }
469              
470             1;
471             # ABSTRACT: perl's type handler for type "hash"
472 24     24 0 45  
473 24         70  
474 24         103 =pod
475 24         43  
476             =encoding UTF-8
477              
478             =head1 NAME
479 24         60  
480 24         141 Data::Sah::Compiler::perl::TH::hash - perl's type handler for type "hash"
481              
482             =head1 VERSION
483              
484             This document describes version 0.914 of Data::Sah::Compiler::perl::TH::hash (from Perl distribution Data-Sah), released on 2022-10-19.
485              
486             =for Pod::Coverage ^(clause_.+|superclause_.+)$
487              
488             =head1 HOMEPAGE
489              
490             Please visit the project's homepage at L<https://metacpan.org/release/Data-Sah>.
491              
492             =head1 SOURCE
493              
494             Source repository is at L<https://github.com/perlancar/perl-Data-Sah>.
495              
496             =head1 AUTHOR
497              
498             perlancar <perlancar@cpan.org>
499              
500             =head1 CONTRIBUTING
501              
502              
503             To contribute, you can send patches by email/via RT, or send pull requests on
504             GitHub.
505              
506             Most of the time, you don't need to build the distribution yourself. You can
507             simply modify the code, then test via:
508              
509             % prove -l
510              
511             If you want to build the distribution (e.g. to try to install it locally on your
512             system), you can install L<Dist::Zilla>,
513             L<Dist::Zilla::PluginBundle::Author::PERLANCAR>,
514             L<Pod::Weaver::PluginBundle::Author::PERLANCAR>, and sometimes one or two other
515             Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond
516             that are considered a bug and can be reported to me.
517              
518             =head1 COPYRIGHT AND LICENSE
519              
520             This software is copyright (c) 2022, 2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013, 2012 by perlancar <perlancar@cpan.org>.
521              
522             This is free software; you can redistribute it and/or modify it under
523             the same terms as the Perl 5 programming language system itself.
524              
525             =head1 BUGS
526              
527             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Sah>
528              
529             When submitting a bug or request, please include a test-file or a
530             patch to an existing test-file that illustrates the bug or desired
531             feature.
532              
533             =cut