File Coverage

blib/lib/Struct/Path/JsonPointer.pm
Criterion Covered Total %
statement 79 79 100.0
branch 38 38 100.0
condition 18 18 100.0
subroutine 10 10 100.0
pod 2 2 100.0
total 147 147 100.0


line stmt bran cond sub pod time code
1             package Struct::Path::JsonPointer;
2              
3 3     3   73044 use 5.006;
  3         25  
4 3     3   18 use strict;
  3         6  
  3         75  
5 3     3   15 use warnings FATAL => 'all';
  3         6  
  3         127  
6 3     3   948 use parent 'Exporter';
  3         596  
  3         24  
7              
8 3     3   178 use Carp 'croak';
  3         6  
  3         161  
9 3     3   19 use Scalar::Util 'looks_like_number';
  3         5  
  3         2786  
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.03
32              
33             =cut
34              
35             our $VERSION = '0.03';
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 17551 croak "Arrayref expected for path" unless (ref $_[0] eq 'ARRAY');
74              
75 11         21 my $str = '';
76 11         20 my $i = 0;
77              
78 11         20 for my $step (@{$_[0]}) {
  11         27  
79 15 100       48 if (ref $step eq 'ARRAY') {
    100          
80             croak "Only one array index allowed, step #$i"
81 4 100       6 if (@{$step} != 1);
  4         105  
82              
83 3 100 100     22 unless (
84             looks_like_number($step->[0])
85             and int($step->[0]) == $step->[0]
86             ) {
87 2         216 croak "Incorrect array index, step #$i";
88             }
89              
90 1         15 $str .= "/$step->[0]";
91             } elsif (ref $step eq 'HASH') {
92             croak "Only keys allowed for hashes, step #$i"
93 10 100 100     18 unless (keys %{$step} == 1 and exists $step->{K});
  10         262  
94              
95             croak "Incorrect hash keys format, step #$i"
96 8 100       161 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         12 $key =~ s|/|~1|g;
104              
105 6         14 $str .= "/$key";
106             } else {
107 1         96 croak "Unsupported thing in the path, step #$i";
108             }
109              
110 7         12 $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   53 my ($step, $last) = @_;
127              
128             return sub {
129 22 100   22   579 if (ref $_ eq 'ARRAY') {
    100          
130 11 100       23 if ($step eq '-') {
131 1         2 $step = @{$_}; # Hyphen as array index should append new item
  1         2  
132             } else {
133 10 100       52 croak "Incorrect array index, step #" . @{$_[0]}
  1         109  
134             unless ($step eq abs(int($step)));
135              
136 2         312 croak "Index is out of range, step #" . @{$_[0]}
137 9 100       28 if ($step > ($_{opts}->{expand} ? @{$_} : $#{$_}));
  2 100       6  
  7         26  
138             }
139              
140 8 100 100     37 splice @{$_}, $step, 0, undef if ($last and $_{opts}->{insert});
  1         4  
141              
142 8         10 push @{$_[0]}, [$step]; # update path
  8         23  
143 8         11 push @{$_[1]}, \$_->[$step]; # update refs stack
  8         21  
144              
145 8 100 100     29 splice @{$_}, $step, 1 if ($last and $_{opts}->{delete});
  1         3  
146              
147             } elsif (ref $_ eq 'HASH') { # HASH
148 2         237 croak "'$step' key doesn't exist, step #" . @{$_[0]}
149 10 100 100     42 unless (exists $_->{$step} or $_{opts}->{expand});
150              
151 8         11 push @{$_[0]}, {K => [$step]}; # update path
  8         26  
152 8         13 push @{$_[1]}, \$_->{$step}; # update refs stack
  8         20  
153             } else {
154 1         2 croak "Structure doesn't match, step #" . @{$_[0]};
  1         107  
155             }
156              
157 16         34 return 1;
158             }
159 22         126 };
160              
161             sub str2path {
162 29     29 1 16064 my @steps = split('/', $_[0], -1);
163 29 100       108 shift @steps if (substr($_[0], 0, 1) eq '/');
164              
165 29         54 my @path;
166              
167 29         56 for my $step (@steps) {
168 39 100 100     187 if (looks_like_number($step) or $step eq '-') {
169 22         52 push @path, _hook($step, @path == $#steps);
170             } else { # hash
171 17         44 $step =~ s|~1|/|g;
172 17         40 $step =~ s|~0|~|g;
173              
174 17         63 push @path, {K => [$step]};
175             }
176             }
177              
178 29         119 return \@path;
179             }
180              
181             =head1 AUTHOR
182              
183             Michael Samoglyadov, C<< >>
184              
185             =head1 BUGS
186              
187             Please report any bugs or feature requests to
188             C, or through the web interface at
189             L. I
190             will be notified, and then you'll automatically be notified of progress on your
191             bug as I make changes.
192              
193             =head1 SUPPORT
194              
195             You can find documentation for this module with the perldoc command.
196              
197             perldoc Struct::Path::JsonPointer
198              
199             You can also look for information at:
200              
201             =over 4
202              
203             =item * RT: CPAN's request tracker (report bugs here)
204              
205             L
206              
207             =item * AnnoCPAN: Annotated CPAN documentation
208              
209             L
210              
211             =item * CPAN Ratings
212              
213             L
214              
215             =item * Search CPAN
216              
217             L
218              
219             =back
220              
221             =head1 SEE ALSO
222              
223             L, L
224              
225             L, L, L
226              
227             =head1 LICENSE AND COPYRIGHT
228              
229             Copyright 2018 Michael Samoglyadov.
230              
231             This program is free software; you can redistribute it and/or modify it under
232             the terms of either: the GNU General Public License as published by the Free
233             Software Foundation; or the Artistic License.
234              
235             See L for more information.
236              
237             =cut
238              
239             1; # End of Struct::Path::JsonPointer