File Coverage

blib/lib/URI/pack.pm
Criterion Covered Total %
statement 86 101 85.1
branch 28 36 77.7
condition 6 12 50.0
subroutine 18 20 90.0
pod 7 7 100.0
total 145 176 82.3


line stmt bran cond sub pod time code
1             package URI::pack;
2              
3 3     3   12028 use 5.008003;
  3         12  
  3         135  
4 3     3   18 use strict;
  3         8  
  3         116  
5 3     3   34 use warnings 'all';
  3         7  
  3         239  
6              
7             ###############################################################################
8             # METADATA
9             our $AUTHORITY = 'cpan:DOUGDUDE';
10             our $VERSION = '0.002001';
11              
12             ###############################################################################
13             # MODULES
14 3     3   16 use Carp qw(croak);
  3         6  
  3         222  
15 3     3   3933 use Const::Fast qw(const);
  3         9420  
  3         19  
16 3     3   294 use URI;
  3         5  
  3         83  
17 3     3   15 use URI::Escape qw(uri_escape uri_unescape);
  3         8  
  3         198  
18              
19             ###############################################################################
20             # INHERIT FROM PARENT CLASS
21 3     3   2816 use parent qw(URI::_generic);
  3         1064  
  3         17  
22              
23             ###############################################################################
24             # CONSTANTS
25             const my $UNRESERVED => qr{[0-9A-Za-z\-\._~]}msx;
26             const my $PCT_ENCODED => qr{\%[0-9A-Fa-f]{2}}msx;
27             const my $SUB_DELIMS => qr{[!\$\&'\(\)\*\+,;=]}msx;
28             const my $PCHAR => qr{(?:$UNRESERVED|$PCT_ENCODED|$SUB_DELIMS|[:\@])}msx;
29              
30             ###############################################################################
31             # ALL IMPORTS BEFORE THIS WILL BE ERASED
32 3     3   32579 use namespace::clean;
  3         59175  
  3         22  
33              
34             ###############################################################################
35             # METHODS
36             sub clear_package_uri {
37 0     0 1 0 my ($self) = @_;
38              
39             # This will remove the package by changing the authority to q{}
40 0         0 $self->authority(q{});
41              
42 0         0 return;
43             }
44             sub clear_part_name {
45 0     0 1 0 my ($self) = @_;
46              
47             # This will remove the part name by changing the path to /
48 0         0 $self->path(q{/});
49              
50 0         0 return;
51             }
52             sub has_package_uri {
53 5     5 1 1659 my ($self) = @_;
54              
55             # Does this URI have a package?
56 5   66     35 return defined $self->authority && $self->authority ne q{};
57             }
58             sub has_part_name {
59 15     15 1 6227 my ($self) = @_;
60              
61             # Does this URI have a part name?
62 15   33     54 return $self->path ne q{} && $self->path ne q{/};
63             }
64             sub package_uri {
65 3     3 1 5068 my ($self, $new_package) = @_;
66              
67             # Get the package according to ECMA-376, Part 2, section B.2
68             # Call the normal authority and get the result
69 3         13 my $authority = $self->authority;
70              
71             # Replace all commas with forward slashes
72 3         53 $authority =~ s{,}{/}gmsx;
73              
74             # Unescape the authority
75 3         19 $authority = uri_unescape($authority);
76              
77 3 50       37 if (defined $new_package) {
78             # Set a new authority according to ECMA-376, Part 2, section B.3
79             # Make sure the new package is a URI
80 0         0 $new_package = URI->new($new_package);
81              
82             # Remove the fragment
83 0         0 $new_package->fragment(q{});
84              
85             # Escape all %, ?, @, :, and , characters
86             ## no critic (ValuesAndExpressions::RequireInterpolationOfMetachars)
87 0         0 $new_package = uri_escape($new_package, '%?@:,');
88              
89             # Replace all forward slashes with commas
90 0         0 $new_package =~ s{/}{,}gmsx;
91              
92             # Set the resulting string as the authority
93 0         0 $self->authority($new_package);
94             }
95              
96             # Return the authority as an URI object
97 3         23 return URI->new($authority);
98             }
99             sub part_name {
100 20     20 1 1505 my ($self, $new_part_name) = @_;
101              
102             # The part name is simply the path
103 20         115 my $part_name = $self->path;
104              
105 20 100       275 if (defined $new_part_name) {
106             # Set the new part name
107 14 50       34 if ($self->_is_valid_part_uri($new_part_name)) {
108             # Set the new part name since it is valid
109 3         11 $self->path($new_part_name);
110             }
111             else {
112 0         0 croak 'The part name given was not a valid part name was thus was not set';
113             }
114             }
115              
116 9 50       144 if (!$self->has_part_name) {
117 0         0 return;
118             }
119              
120 9         248 return $part_name;
121             }
122             sub part_name_segments {
123 4     4 1 33 my ($self, @new_part_name_segments) = @_;
124              
125             # Get the path segments
126 4         30 my @path_segments = $self->path_segments;
127              
128             # Remove the first path segment, as it is q{}
129 4 50 33     376 if (@path_segments && $path_segments[0] eq q{}) {
130 4         5 shift @path_segments;
131             }
132              
133 4 100       12 if (@new_part_name_segments) {
134             # Set the new part name
135 1         6 $self->part_name(q{/} . join q{/}, @new_part_name_segments);
136             }
137              
138 4         31 return @path_segments;
139             }
140              
141             ###############################################################################
142             # PRIVATE METHODS
143             sub _check_uri {
144 3     3   8 my ($self) = @_;
145              
146             # If the URI has a part name, check it
147 3 50       10 if ($self->has_part_name) {
148             # Check the part
149 3         325 $self->_is_valid_part_uri($self->path);
150             }
151              
152             # Must have either package or part name
153 3 50 66     18 if (!$self->has_package_uri && !$self->has_part_name) {
154 0         0 croak 'Not a valid URI';
155             }
156              
157 3         183 return $self;
158             }
159             sub _init { ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
160 3     3   1066 my ($class, $uri, $scheme) = @_;
161              
162             # Create and bless into class using default _init
163 3         24 my $self = $class->SUPER::_init($uri, $scheme);
164              
165             # Check the URI
166 3         171 $self->_check_uri();
167              
168 3         17 return $self;
169             }
170             sub _is_valid_part_uri {
171 17     17   63 my ($self, $part_uri) = @_;
172              
173             # Validate a part URI according to ECMA-376 Part 2, section 9.1.1.1.2
174              
175 17 100       50 if ($part_uri eq q{}) {
176 1         16 croak 'A part URI shall not be empty [M1.1]';
177             }
178              
179 16 100       76 if ($part_uri !~ m{\A /}msx) {
180 1         13 croak 'A part URI shall start with a forward slash ("/") character [M1.4]';
181             }
182              
183 15 100       44 if ($part_uri =~ m{/ \z}msx) {
184 1         12 croak 'A part URI shall not have a forward slash as the last character [M1.5]';
185             }
186              
187             # Split the part URI into segments
188 14         66 my @segments = split m{/}msx, $part_uri;
189              
190             # Remove the first empty segment
191 14 50       45 if ($segments[0] eq q{}) {
192 14         23 shift @segments;
193             }
194              
195 14         29 foreach my $segment (@segments) {
196 24 100       61 if ($segment eq q{}) {
197 2         36 croak 'A part URI shall not have empty segments [M1.3]';
198             }
199              
200 22 100       283 if ($segment !~ m{\A (?:$PCHAR)+ \z}msx) {
201 1         11 croak 'A segments shall not hold any characters other than pchar characters [M1.6]';
202             }
203              
204 21 100       73 if ($segment =~ m{\%(?:2f|5c)}imsx) {
205 2         27 croak 'A segments shall not contain percent-encoded forward slash ("/"), or backward slash ("\") characters [M1.7]';
206             }
207              
208 19         55 while ($segment =~ m{%([0-9a-f]{2})}gimsx) {
209             # Convert the byte into the original character
210 3         14 my $character = chr hex $1;
211              
212 3 100       17 if ($character =~ m{\A [0-9A-Z\-\._~] \z}imsx) {
213 1         124 croak 'A segment shall not contain percent-encoded unreserved characters [M1.8]';
214             }
215             }
216              
217 18 100       47 if ($segment =~ m{\. \z}msx) {
218 2         33 croak 'A segment shall not end with a dot (".") character [M1.9]';
219             }
220              
221 16 50       79 if ($segment !~ m{[^\.]+}msx) {
222 0         0 croak 'A segment shall include at least one non-dot character [M1.10]';
223             }
224             }
225              
226 6         23 return 1;
227             }
228 1     1   20 sub _no_scheme_ok { return 0; } ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
229              
230             1;
231              
232             __END__