File Coverage

blib/lib/Struct/Path/JsonPointer.pm
Criterion Covered Total %
statement 80 80 100.0
branch 40 40 100.0
condition 20 21 95.2
subroutine 10 10 100.0
pod 2 2 100.0
total 152 153 99.3


line stmt bran cond sub pod time code
1             package Struct::Path::JsonPointer;
2              
3 3     3   70260 use 5.006;
  3         24  
4 3     3   18 use strict;
  3         6  
  3         72  
5 3     3   16 use warnings FATAL => 'all';
  3         6  
  3         117  
6 3     3   867 use parent 'Exporter';
  3         559  
  3         27  
7              
8 3     3   226 use Carp 'croak';
  3         8  
  3         155  
9 3     3   18 use Scalar::Util 'looks_like_number';
  3         7  
  3         2875  
10              
11             our @EXPORT_OK = qw(
12             path2str
13             str2path
14             );
15              
16             =head1 NAME
17              
18             Struct::Path::JsonPointer - JsonPointer (L)
19             syntax frontend for L
20              
21             =begin html
22              
23             Travis CI
24             Coverage Status
25             CPAN version
26              
27             =end html
28              
29             =head1 VERSION
30              
31             Version 0.04
32              
33             =cut
34              
35             our $VERSION = '0.04';
36              
37             =head1 SYNOPSIS
38              
39             use Struct::Path qw(path);
40             use Struct::Path::JsonPointer qw(str2path);
41              
42             my $data = {
43             "foo" => ["bar", "baz"],
44             "" => 0,
45             "a/b" => 1,
46             "c%d" => 2,
47             "e^f" => 3,
48             "g|h" => 4,
49             "i\\j" => 5,
50             "k\"l" => 6,
51             " " => 7,
52             "m~n" => 8
53             };
54              
55             my ($found) = path($data, str2path('/foo/0'), deref => 1);
56             print $found; # 'bar'
57              
58             =head1 EXPORT
59              
60             Nothing is exported by default.
61              
62             =head1 SUBROUTINES
63              
64             =head2 path2str
65              
66             Convert L path to JsonPointer.
67              
68             $pointer = path2str($path);
69              
70             =cut
71              
72             sub path2str {
73 12 100   12 1 16895 croak "Arrayref expected for path" unless (ref $_[0] eq 'ARRAY');
74              
75 11         22 my $str = '';
76 11         16 my $i = 0;
77              
78 11         20 for my $step (@{$_[0]}) {
  11         29  
79 15 100       44 if (ref $step eq 'ARRAY') {
    100          
80             croak "Only one array index allowed, step #$i"
81 4 100       6 if (@{$step} != 1);
  4         107  
82              
83 3 100 100     21 unless (
84             looks_like_number($step->[0])
85             and int($step->[0]) == $step->[0]
86             ) {
87 2         212 croak "Incorrect array index, step #$i";
88             }
89              
90 1         11 $str .= "/$step->[0]";
91             } elsif (ref $step eq 'HASH') {
92             croak "Only keys allowed for hashes, step #$i"
93 10 100 100     14 unless (keys %{$step} == 1 and exists $step->{K});
  10         256  
94              
95             croak "Incorrect hash keys format, step #$i"
96 8 100       136 unless (ref $step->{K} eq 'ARRAY');
97              
98             croak "Only one hash key allowed, step #$i"
99 7 100       11 unless (@{$step->{K}} == 1);
  7         118  
100              
101 6         11 my $key = $step->{K}->[0];
102 6         16 $key =~ s|~|~0|g;
103 6         13 $key =~ s|/|~1|g;
104              
105 6         13 $str .= "/$key";
106             } else {
107 1         95 croak "Unsupported thing in the path, step #$i";
108             }
109              
110 7         11 $i++;
111             }
112              
113 3         10 return $str;
114             }
115              
116             =head2 str2path
117              
118             Convert JsonPointer to L path.
119              
120             $path = str2path($pointer);
121              
122             =cut
123              
124             # some steps (numbers, dash) should be evaluated using structure
125             sub _hook {
126 22     22   54 my ($step, $last) = @_;
127              
128             return sub {
129 22 100   22   577 if (ref $_ eq 'ARRAY') {
    100          
130 11 100       43 if ($step eq '-') {
131 1         1 $step = @{$_}; # Hyphen as array index should append new item
  1         3  
132             } else {
133 10 100       36 croak "Incorrect array index, step #" . @{$_[0]}
  1         105  
134             unless ($step eq abs(int($step)));
135              
136 2         310 croak "Index is out of range, step #" . @{$_[0]}
137 9 100       25 if ($step > ($_{opts}->{expand} ? @{$_} : $#{$_}));
  2 100       6  
  7         20  
138             }
139              
140 8 100 100     34 splice @{$_}, $step, 0, undef if ($last and $_{opts}->{insert});
  1         3  
141              
142 8         12 push @{$_[0]}, [$step]; # update path
  8         19  
143 8         15 push @{$_[1]}, \$_->[$step]; # update refs stack
  8         19  
144              
145 8 100 100     28 splice @{$_}, $step, 1 if ($last and $_{opts}->{delete});
  1         2  
146              
147             } elsif (ref $_ eq 'HASH') {
148 2         213 croak "'$step' key doesn't exist, step #" . @{$_[0]}
149 10 100 100     37 unless (exists $_->{$step} or $_{opts}->{expand});
150              
151 8         11 push @{$_[0]}, {K => [$step]}; # update path
  8         24  
152 8         16 push @{$_[1]}, \$_->{$step}; # update refs stack
  8         18  
153             } else {
154 1         2 croak "Structure doesn't match, step #" . @{$_[0]};
  1         103  
155             }
156              
157 16         33 return 1;
158             }
159 22         118 };
160              
161             sub str2path {
162 31 100   31 1 17093 croak "Undefined JSON Pointer passed" unless (defined $_[0]);
163              
164 30         102 my @steps = split('/', $_[0], -1);
165              
166 30 100 66     189 croak "JSON Pointer should start with a slash or be empty"
167             if (shift @steps and substr($_[0], 0, 1) ne '/');
168              
169 29         45 my @path;
170              
171 29         60 for (@steps) {
172 39 100 100     184 if (looks_like_number($_) or $_ eq '-') {
173 22         52 push @path, _hook($_, @path == $#steps);
174             } else { # hash
175 17         37 s|~1|/|g;
176 17         37 s|~0|~|g;
177              
178 17         58 push @path, {K => [$_]};
179             }
180             }
181              
182 29         119 return \@path;
183             }
184              
185             =head1 AUTHOR
186              
187             Michael Samoglyadov, C<< >>
188              
189             =head1 BUGS
190              
191             Please report any bugs or feature requests to
192             C, or through the web interface at
193             L. I
194             will be notified, and then you'll automatically be notified of progress on your
195             bug as I make changes.
196              
197             =head1 SUPPORT
198              
199             You can find documentation for this module with the perldoc command.
200              
201             perldoc Struct::Path::JsonPointer
202              
203             You can also look for information at:
204              
205             =over 4
206              
207             =item * RT: CPAN's request tracker (report bugs here)
208              
209             L
210              
211             =item * AnnoCPAN: Annotated CPAN documentation
212              
213             L
214              
215             =item * CPAN Ratings
216              
217             L
218              
219             =item * Search CPAN
220              
221             L
222              
223             =back
224              
225             =head1 SEE ALSO
226              
227             L, L
228              
229             L, L, L
230              
231             =head1 LICENSE AND COPYRIGHT
232              
233             Copyright 2018 Michael Samoglyadov.
234              
235             This program is free software; you can redistribute it and/or modify it under
236             the terms of either: the GNU General Public License as published by the Free
237             Software Foundation; or the Artistic License.
238              
239             See L for more information.
240              
241             =cut
242              
243             1; # End of Struct::Path::JsonPointer