File Coverage

blib/lib/Struct/Path/JsonPointer.pm
Criterion Covered Total %
statement 72 72 100.0
branch 32 32 100.0
condition 9 9 100.0
subroutine 10 10 100.0
pod 2 2 100.0
total 125 125 100.0


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