File Coverage

blib/lib/Net/Works/Address.pm
Criterion Covered Total %
statement 78 81 96.3
branch 17 22 77.2
condition 10 16 62.5
subroutine 21 24 87.5
pod 8 10 80.0
total 134 153 87.5


line stmt bran cond sub pod time code
1             package Net::Works::Address;
2             $Net::Works::Address::VERSION = '0.20';
3 4     4   52218 use strict;
  4         7  
  4         135  
4 4     4   18 use warnings;
  4         6  
  4         117  
5              
6 4     4   19 use Carp qw( confess );
  4         10  
  4         261  
7 4     4   18 use Math::Int128 0.06 qw( uint128 uint128_to_hex uint128_to_number );
  4         76  
  4         248  
8 4     4   1345 use Net::Works::Types qw( PackedBinary Str );
  4         8  
  4         246  
9 4         239 use Net::Works::Util qw(
10             _integer_address_to_binary
11             _integer_address_to_string
12             _string_address_to_integer
13             _validate_ip_string
14 4     4   1443 );
  4         32  
15 4     4   18 use Scalar::Util qw( blessed );
  4         4  
  4         165  
16 4     4   17 use Socket 1.99 qw( AF_INET AF_INET6 inet_pton inet_ntop );
  4         65  
  4         167  
17              
18 4     4   2112 use integer;
  4         33  
  4         15  
19              
20             # Using this currently breaks overloading - see
21             # https://rt.cpan.org/Ticket/Display.html?id=50938
22             #
23             #use namespace::autoclean;
24              
25             use overload (
26 4         26 q{""} => '_overloaded_as_string',
27             '<=>' => '_compare_overload',
28             'cmp' => '_compare_overload',
29 4     4   145 );
  4         5  
30              
31 4     4   3441 use Moo;
  4         12888  
  4         31  
32              
33             with 'Net::Works::Role::IP';
34              
35             has _binary => (
36             is => 'ro',
37             reader => 'as_binary',
38             isa => PackedBinary,
39             lazy => 1,
40             builder => '_build_binary',
41             );
42              
43             has _string => (
44             is => 'ro',
45             reader => 'as_string',
46             isa => Str,
47             lazy => 1,
48             builder => '_build_string',
49             );
50              
51             sub BUILD {
52 2523     2523 0 128026 my $self = shift;
53              
54 2523         5609 $self->_validate_ip_integer();
55              
56 2521         40325 return;
57             }
58              
59             sub new_from_string {
60 127     127 1 32014 my $class = shift;
61 127         290 my %p = @_;
62              
63 127         221 my $str = delete $p{string};
64 127         171 my $version = delete $p{version};
65              
66 127 100 100     706 if ( defined $str && inet_pton( AF_INET, $str ) ) {
67 43   100     103 $version ||= 4;
68 43 100       97 $str = '::' . $str if $version == 6;
69             }
70             else {
71 84   100     180 $version ||= 6;
72 84         193 _validate_ip_string( $str, $version );
73             }
74              
75 110         245 return $class->new(
76             _integer => _string_address_to_integer( $str, $version ),
77             version => $version,
78             %p,
79             );
80             }
81              
82             sub new_from_integer {
83 2423     2423 1 18252 my $class = shift;
84 2423         5106 my %p = @_;
85              
86 2423         3638 my $int = delete $p{integer};
87 2423         2653 my $version = delete $p{version};
88 2423 0 33     4129 $version ||= ref $int ? 6 : 4;
89              
90 2423         45789 return $class->new(
91             _integer => $int,
92             version => $version,
93             %p,
94             );
95             }
96              
97             sub _build_string {
98 2006     2006   169025 my $self = shift;
99              
100 2006         4353 return _integer_address_to_string( $self->_integer() );
101             }
102              
103 0     0   0 sub _build_binary { _integer_address_to_binary( $_[0]->as_integer() ) }
104              
105 1846     1846 1 25569 sub as_integer { $_[0]->_integer() }
106              
107             sub as_ipv4_string {
108 657     657 1 23209 my $self = shift;
109              
110 657 100       2057 return $self->as_string() if $self->version() == 4;
111              
112 623 100       951 confess
113             'Cannot represent IP address larger than 2**32-1 as an IPv4 string'
114             if $self->as_integer() >= 2**32;
115              
116 250         6471 return __PACKAGE__->new_from_integer(
117             integer => $self->as_integer(),
118             version => 4,
119             )->as_string();
120             }
121              
122             sub as_bit_string {
123 5     5 1 2736 my $self = shift;
124              
125 5 100       21 if ( $self->version == 6 ) {
126 3         7 my $hex = uint128_to_hex( $self->as_integer() );
127 3         118 my @ha = $hex =~ /.{8}/g;
128 3         7 return join '', map { sprintf( '%032b', hex($_) ) } @ha;
  12         47  
129             }
130             else {
131 2         6 return sprintf( '%032b', $self->as_integer() );
132             }
133             }
134              
135 0     0 1 0 sub prefix_length { $_[0]->bits() }
136              
137 0     0 0 0 sub mask_length { $_[0]->prefix_length() }
138              
139             sub next_ip {
140 5     5 1 965 my $self = shift;
141              
142 5 100       12 confess "$self is the last address in its range"
143             if $self->as_integer() == $self->_max;
144              
145 3         7 return __PACKAGE__->new_from_integer(
146             integer => $self->as_integer() + 1,
147             version => $self->version(),
148             );
149             }
150              
151             sub previous_ip {
152 4     4 1 802 my $self = shift;
153              
154 4 100       10 confess "$self is the first address in its range"
155             if $self->as_integer() == 0;
156              
157 2         68 return __PACKAGE__->new_from_integer(
158             integer => $self->as_integer() - 1,
159             version => $self->version(),
160             );
161             }
162              
163             sub _compare_overload {
164 211     211   4881 my $self = shift;
165 211         207 my $other = shift;
166 211 50       273 my $flip = shift() ? -1 : 1;
167              
168             confess 'Cannot compare unless both objects are '
169             . __PACKAGE__
170             . ' objects'
171             unless blessed $self
172             && blessed $other
173 211 50 33     1090 && eval { $self->isa(__PACKAGE__) && $other->isa(__PACKAGE__) };
  211 50 33     1187  
174              
175 211         349 return $flip * ( $self->as_integer() <=> $other->as_integer() );
176             }
177              
178             __PACKAGE__->meta()->make_immutable();
179              
180             1;
181              
182             # ABSTRACT: An object representing a single IP (4 or 6) address
183              
184             __END__