File Coverage

blib/lib/Perinci/Sub/Normalize.pm
Criterion Covered Total %
statement 61 75 81.3
branch 33 46 71.7
condition 33 38 86.8
subroutine 5 5 100.0
pod 1 1 100.0
total 133 165 80.6


line stmt bran cond sub pod time code
1              
2             our $DATE = '2021-08-01'; # DATE
3             our $VERSION = '0.202'; # VERSION
4              
5             use 5.010001;
6 2     2   923 use strict;
  2         14  
7 2     2   9 use warnings;
  2         3  
  2         43  
8 2     2   8  
  2         3  
  2         2191  
9             require Exporter;
10             our @ISA = qw(Exporter);
11             our @EXPORT_OK = qw(
12             normalize_function_metadata
13             );
14              
15             my ($meta, $ver, $opts, $proplist, $nmeta, $prefix, $modprefix) = @_;
16              
17 39     39   93 my $opt_aup = $opts->{allow_unknown_properties};
18             my $opt_nss = $opts->{normalize_sah_schemas};
19 39         59 my $opt_rip = $opts->{remove_internal_properties};
20 39         51  
21 39         46 if (defined $ver) {
22             defined($meta->{v}) && $meta->{v} eq $ver
23 39 100       75 or die "$prefix: Metadata version must be $ver";
24 22 100 66     166 }
25              
26             KEY:
27             for my $k (keys %$meta) {
28             die "Invalid prop/attr syntax '$k', must be word/dotted-word only"
29 37         92 unless $k =~ /\A(\w+)(?:\.(\w+(?:\.\w+)*))?(?:\((\w+)\))?\z/;
30 63 100       300  
31             my ($prop, $attr);
32             if (defined $3) {
33 62         89 $prop = $1;
34 62 100       107 $attr = defined($2) ? "$2.alt.lang.$3" : "alt.lang.$3";
35 1         2 } else {
36 1 50       4 $prop = $1;
37             $attr = $2;
38 61         94 }
39 61         82  
40             my $nk = "$prop" . (defined($attr) ? ".$attr" : "");
41              
42 62 100       113 # strip property/attr started with _
43             if ($prop =~ /\A_/ || defined($attr) && $attr =~ /\A_|\._/) {
44             unless ($opt_rip) {
45 62 100 100     209 $nmeta->{$nk} = $meta->{$k};
      100        
46 5 100       9 }
47 4         6 next KEY;
48             }
49 5         9  
50             my $prop_proplist = $proplist->{$prop};
51              
52 57         86 # try to load module that declare new props first
53             if (!$opt_aup && !$prop_proplist) {
54             $modprefix //= $prefix;
55 57 100 100     142 my $mod = "Perinci/Sub/Property$modprefix/$prop.pm";
56 4   66     18 eval { require $mod };
57 4         11 # hide technical error message from require()
58 4         6 if ($@) {
  4         511  
59             die "Unknown property '$prefix/$prop' (and couldn't ".
60 4 50       28 "load property module '$mod'): $@" if $@;
61 4 50       55 }
62             $prop_proplist = $proplist->{$prop};
63             }
64 0         0 die "Unknown property '$prefix/$prop'"
65             unless $opt_aup || $prop_proplist;
66 53 50 66     124  
67             if ($prop_proplist && $prop_proplist->{_prop}) {
68             die "Property '$prefix/$prop' must be a hash"
69 53 100 100     210 unless ref($meta->{$k}) eq 'HASH';
    50 66        
    100 66        
70             $nmeta->{$nk} = {};
71 9 50       25 _normalize(
72 9         19 $meta->{$k},
73             $prop_proplist->{_ver},
74             $opts,
75             $prop_proplist->{_prop},
76             $nmeta->{$nk},
77             "$prefix/$prop",
78 9         53 );
79             } elsif ($prop_proplist && $prop_proplist->{_elem_prop}) {
80             die "Property '$prefix/$prop' must be an array"
81             unless ref($meta->{$k}) eq 'ARRAY';
82             $nmeta->{$nk} = [];
83 0 0       0 my $i = 0;
84 0         0 for (@{ $meta->{$k} }) {
85 0         0 my $href = {};
86 0         0 if (ref($_) eq 'HASH') {
  0         0  
87 0         0 _normalize(
88 0 0       0 $_,
89             $prop_proplist->{_ver},
90             $opts,
91             $prop_proplist->{_elem_prop},
92             $href,
93             "$prefix/$prop/$i",
94 0         0 );
95             push @{ $nmeta->{$nk} }, $href;
96             } else {
97 0         0 push @{ $nmeta->{$nk} }, $_;
  0         0  
98             }
99 0         0 $i++;
  0         0  
100             }
101 0         0 } elsif ($prop_proplist && $prop_proplist->{_value_prop}) {
102             die "Property '$prefix/$prop' must be a hash"
103             unless ref($meta->{$k}) eq 'HASH';
104             $nmeta->{$nk} = {};
105 10 50       24 for (keys %{ $meta->{$k} }) {
106 10         33 $nmeta->{$nk}{$_} = {};
107 10         17 die "Property '$prefix/$prop/$_' must be a hash"
  10         26  
108 12         22 unless ref($meta->{$k}{$_}) eq 'HASH';
109             _normalize(
110 12 50       26 $meta->{$k}{$_},
111             $prop_proplist->{_ver},
112             $opts,
113             $prop_proplist->{_value_prop},
114             $nmeta->{$nk}{$_},
115             "$prefix/$prop/$_",
116 12 100       102 ($prop eq 'args' ? "$prefix/arg" : undef),
117             );
118             }
119             } else {
120             if ($k eq 'schema' && $opt_nss) { # XXX currently hardcoded
121             require Data::Sah::Normalize;
122 34 100 100     83 $nmeta->{$nk} = Data::Sah::Normalize::normalize_schema(
123 8         36 $meta->{$k});
124             } else {
125 8         20 $nmeta->{$nk} = $meta->{$k};
126             }
127 26         59 }
128             }
129              
130             $nmeta;
131             }
132 27         294  
133             my ($meta, $opts) = @_;
134              
135             $opts //= {};
136 18     18 1 64518  
137             $opts->{allow_unknown_properties} //= 0;
138 18   100     84 $opts->{normalize_sah_schemas} //= 1;
139             $opts->{remove_internal_properties} //= 0;
140 18   100     70  
141 18   100     62 require Sah::Schema::rinci::function_meta;
142 18   100     55 my $sch = $Sah::Schema::rinci::function_meta::schema;
143             my $sch_proplist = $sch->[1]{_prop}
144 18         504 or die "BUG: Rinci schema structure changed (1a)";
145 18         2887  
146             _normalize($meta, 1.1, $opts, $sch_proplist, {}, '');
147 18 50       43 }
148              
149 18         39 1;
150             # ABSTRACT: Normalize Rinci function metadata
151              
152              
153             =pod
154              
155             =encoding UTF-8
156              
157             =head1 NAME
158              
159             Perinci::Sub::Normalize - Normalize Rinci function metadata
160              
161             =head1 VERSION
162              
163             This document describes version 0.202 of Perinci::Sub::Normalize (from Perl distribution Perinci-Sub-Normalize), released on 2021-08-01.
164              
165             =head1 SYNOPSIS
166              
167             use Perinci::Sub::Normalize qw(normalize_function_metadata);
168              
169             my $nmeta = normalize_function_metadata($meta);
170              
171             =head1 CONTRIBUTOR
172              
173             =for stopwords Steven Haryanto
174              
175             Steven Haryanto <sharyanto@cpan.org>
176              
177             =head1 FUNCTIONS
178              
179             =head2 normalize_function_metadata($meta[ , \%opts ]) => HASH
180              
181             Normalize and check L<Rinci> function metadata C<$meta>. Return normalized
182             metadata, which is a shallow copy of C<$meta>. Die on error.
183              
184             Available options:
185              
186             =over
187              
188             =item * allow_unknown_properties => BOOL (default: 0)
189              
190             If set to true, will die if there are unknown properties.
191              
192             =item * normalize_sah_schemas => BOOL (default: 1)
193              
194             By default, L<Sah> schemas e.g. in C<result/schema> or C<args/*/schema> property
195             is normalized using L<Data::Sah>'s C<normalize_schema>. Set this to 0 if you
196             don't want this.
197              
198             =item * remove_internal_properties => BOOL (default: 0)
199              
200             If set to 1, all properties and attributes starting with underscore (C<_>) with
201             will be stripped. According to L<DefHash> specification, they are ignored and
202             usually contain notes/comments/extra information.
203              
204             =back
205              
206             =head1 HOMEPAGE
207              
208             Please visit the project's homepage at L<https://metacpan.org/release/Perinci-Sub-Normalize>.
209              
210             =head1 SOURCE
211              
212             Source repository is at L<https://github.com/perlancar/perl-Perinci-Sub-Normalize>.
213              
214             =head1 BUGS
215              
216             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-Sub-Normalize>
217              
218             When submitting a bug or request, please include a test-file or a
219             patch to an existing test-file that illustrates the bug or desired
220             feature.
221              
222             =head1 SEE ALSO
223              
224             L<Rinci::function>
225              
226             =head1 AUTHOR
227              
228             perlancar <perlancar@cpan.org>
229              
230             =head1 COPYRIGHT AND LICENSE
231              
232             This software is copyright (c) 2021, 2018, 2016, 2015, 2014 by perlancar@cpan.org.
233              
234             This is free software; you can redistribute it and/or modify it under
235             the same terms as the Perl 5 programming language system itself.
236              
237             =cut