File Coverage

blib/lib/Business/UPS/Tracking/Utils.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             # ============================================================================
2             package Business::UPS::Tracking::Utils;
3             # ============================================================================
4 1     1   15675 use utf8;
  1         5  
  1         11  
5 1     1   1202 use 5.0100;
  1         4  
  1         42  
6              
7 1     1   7 use strict;
  1         2  
  1         38  
8 1     1   6 use warnings;
  1         2  
  1         53  
9              
10 1     1   4402 use Try::Tiny;
  1         10522  
  1         75  
11 1     1   1769 use Business::UPS::Tracking::Exception;
  1         5  
  1         45  
12 1     1   551 use Moose::Util::TypeConstraints;
  0            
  0            
13             use Business::UPS::Tracking;
14             use MooseX::Getopt::OptionTypeMap;
15             use Business::UPS::Tracking::Meta::Attribute::Trait::Printable;
16             use Encode;
17              
18             =encoding utf8
19              
20             =head1 NAME
21              
22             Business::UPS::Tracking::Utils - Utility functions
23              
24             =head1 SYNOPSIS
25              
26             use Business::UPS::Tracking::Utils;
27            
28             =head1 DESCRIPTION
29              
30             This module provides some basic utility functions for
31             L<Business::UPS::Tracking> and defines some Moose type constraints and
32             coercions.
33            
34             =head1 FUNCTIONS
35              
36             =cut
37              
38             subtype 'Business::UPS::Tracking::Type::XMLDocument'
39             => as class_type('XML::LibXML::Document');
40              
41             coerce 'Business::UPS::Tracking::Type::XMLDocument'
42             => from 'Str'
43             => via {
44             my $xml = $_;
45             $xml = decode("iso-8859-1", $xml);
46            
47             my $parser = XML::LibXML->new(
48             #encoding => 'iso-8859-15'
49             );
50             return try {
51             return $parser->parse_string($xml);
52             } catch {
53             Business::UPS::Tracking::X::XML->throw(
54             error => $_ || 'Unknown error parsing xml document',
55             xml => $xml,
56             );
57             }
58             };
59            
60             subtype 'Business::UPS::Tracking::Type::Date'
61             => as class_type('DateTime');
62              
63             subtype 'Business::UPS::Tracking::Type::DateStr'
64             => as 'Str'
65             => where {
66             m/^
67             (19|20)\d\d #year
68             (0[1-9]|1[012]) #month
69             (3[01]|[12]\d|0[1-9]) #day
70             $/x;
71             };
72              
73             coerce 'Business::UPS::Tracking::Type::DateStr'
74             => from 'Business::UPS::Tracking::Type::Date'
75             => via {
76             return $_->format_cldr('yyyyMMdd');
77             };
78              
79             subtype 'Business::UPS::Tracking::Type::TrackingNumber'
80             => as 'Str'
81             => where {
82             my $trackingnumber = $_;
83             return 0
84             unless ($trackingnumber =~ m/^1Z(?<tracking>[A-Z0-9]{8}\d{7})(?<checksum>\d)$/);
85             # Checksum check fails because UPS testdata has invalid checksum!
86             return 1
87             unless $Business::UPS::Tracking::CHECKSUM;
88             my $checksum = $+{checksum};
89             my $tracking = $+{tracking};
90             $tracking =~ tr/ABCDEFGHIJKLMNOPQRSTUVWXYZ/23456789012345678901234567/;
91             my ($odd,$even,$pos) = (0,0,0);
92             foreach (split //,$tracking) {
93             $pos ++;
94             if ($pos % 2) {
95             $odd += $_;
96             } else {
97             $even += $_;
98             }
99             }
100             $even *= 2;
101             my $calculated = $odd + $even;
102             $calculated =~ s/^\d+(\d)$/$1/e;
103             $calculated = 10 - $calculated
104             unless ($calculated == 0);
105             return ($checksum == $calculated);
106             }
107             => message { "Tracking numbers must start withn '1Z', contain 15 additional characters and end with a valid checksum : '$_'" };
108              
109             subtype 'Business::UPS::Tracking::Type::CountryCode'
110             => as 'Str'
111             => where { m/^[A-Z]{2}$/ }
112             => message { "Must be an uppercase ISO 3166-1 alpha-2 code" };
113              
114             =head3 parse_date
115              
116             $datetime = parse_date($string);
117              
118             Parses a date string (YYYYMMDD) and returns a L<DateTime> object.
119              
120             =cut
121              
122             sub parse_date {
123             my $datestr = shift;
124            
125             return
126             unless $datestr
127             && $datestr =~ m/^
128             (?<year>(19|20)\d\d)
129             (?<month>0[1-9]|1[012])
130             (?<day>3[01]|[12]\d|0[1-9])
131             $/x;
132            
133             return try {
134             DateTime->new(
135             year => $+{year},
136             month => $+{month},
137             day => $+{day},
138             );
139             } catch {
140             Business::UPS::Tracking::X::XML->throw(
141             error => "Invalid date string: ".$_,
142             xml => $datestr,
143             );
144             };
145             }
146              
147             =head3 parse_time
148              
149             $datetime = parse_time($string,$datetime);
150              
151             Parses a time string (HHMMSS) and appends the parsed values to the given
152             L<DateTime> object
153              
154             =cut
155              
156             sub parse_time {
157             my ($timestr,$datetime) = @_;
158            
159             return
160             unless $datetime;
161            
162             return $datetime
163             unless $timestr
164             && $timestr =~ m/^
165             (?<hour>\d\d)
166             (?<minute>\d\d)
167             (?<second>\d\d)
168             $/x;
169            
170             try {
171             $datetime->set_hour( $+{hour} );
172             $datetime->set_minute( $+{minute} );
173             $datetime->set_second( $+{second} );
174             return 1;
175             } catch {
176             Business::UPS::Tracking::X::XML->throw(
177             error => "Invalid time string: ".$_,
178             xml => $timestr,
179             );
180             }
181              
182             return $datetime;
183             }
184              
185              
186             =head3 escape_xml
187              
188             my $escaped_string = escape_xml($string);
189              
190             Escapes a string for xml
191              
192             =cut
193              
194             sub escape_xml {
195             my ($string) = @_;
196            
197             $string =~ s/&/&amp;/g;
198             $string =~ s/</&gt;/g;
199             $string =~ s/>/&lt;/g;
200             $string =~ s/"/&qout;/g;
201             $string =~ s/'/&apos;/g;
202            
203             return $string;
204             }
205              
206             1;