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              
3 4     4   155953 use strict;
  4         6  
  4         98  
4 4     4   14 use warnings;
  4         5  
  4         128  
5              
6             our $VERSION = '0.22';
7              
8 4     4   13 use Carp qw( confess );
  4         5  
  4         220  
9 4     4   14 use Math::Int128 0.06 qw( uint128 uint128_to_hex uint128_to_number );
  4         62  
  4         165  
10 4     4   1221 use Net::Works::Types qw( PackedBinary Str );
  4         6  
  4         221  
11 4         204 use Net::Works::Util qw(
12             _integer_address_to_binary
13             _integer_address_to_string
14             _string_address_to_integer
15             _validate_ip_string
16 4     4   1386 );
  4         6  
17 4     4   15 use Scalar::Util qw( blessed );
  4         4  
  4         137  
18 4     4   14 use Socket 1.99 qw( AF_INET AF_INET6 inet_pton inet_ntop );
  4         53  
  4         152  
19              
20 4     4   1231 use integer;
  4         25  
  4         17  
21              
22             # Using this currently breaks overloading - see
23             # https://rt.cpan.org/Ticket/Display.html?id=50938
24             #
25             #use namespace::autoclean;
26              
27             use overload (
28 4         21 q{""} => '_overloaded_as_string',
29             '<=>' => '_compare_overload',
30             'cmp' => '_compare_overload',
31 4     4   115 );
  4         4  
32              
33 4     4   1852 use Moo;
  4         7915  
  4         58  
34              
35             with 'Net::Works::Role::IP';
36              
37             has _binary => (
38             is => 'ro',
39             reader => 'as_binary',
40             isa => PackedBinary,
41             lazy => 1,
42             builder => '_build_binary',
43             );
44              
45             has _string => (
46             is => 'ro',
47             reader => 'as_string',
48             isa => Str,
49             lazy => 1,
50             builder => '_build_string',
51             );
52              
53             sub BUILD {
54 2525     2525 0 102400 my $self = shift;
55              
56 2525         5200 $self->_validate_ip_integer();
57              
58 2523         32070 return;
59             }
60              
61             sub new_from_string {
62 127     127 1 24962 my $class = shift;
63 127         257 my %p = @_;
64              
65 127         162 my $str = delete $p{string};
66 127         135 my $version = delete $p{version};
67              
68 127 100 100     637 if ( defined $str && inet_pton( AF_INET, $str ) ) {
69 43   100     127 $version ||= 4;
70 43 100       91 $str = '::' . $str if $version == 6;
71             }
72             else {
73 84   100     167 $version ||= 6;
74 84         163 _validate_ip_string( $str, $version );
75             }
76              
77 110         219 return $class->new(
78             _integer => _string_address_to_integer( $str, $version ),
79             version => $version,
80             %p,
81             );
82             }
83              
84             sub new_from_integer {
85 2425     2425 1 16406 my $class = shift;
86 2425         4312 my %p = @_;
87              
88 2425         2577 my $int = delete $p{integer};
89 2425         2149 my $version = delete $p{version};
90 2425 0 33     3721 $version ||= ref $int ? 6 : 4;
91              
92 2425         34847 return $class->new(
93             _integer => $int,
94             version => $version,
95             %p,
96             );
97             }
98              
99             sub _build_string {
100 2008     2008   138721 my $self = shift;
101              
102 2008         3522 return _integer_address_to_string( $self->_integer() );
103             }
104              
105 0     0   0 sub _build_binary { _integer_address_to_binary( $_[0]->as_integer() ) }
106              
107 1848     1848 1 18595 sub as_integer { $_[0]->_integer() }
108              
109             sub as_ipv4_string {
110 657     657 1 17920 my $self = shift;
111              
112 657 100       1777 return $self->as_string() if $self->version() == 4;
113              
114 623 100       880 confess
115             'Cannot represent IP address larger than 2**32-1 as an IPv4 string'
116             if $self->as_integer() >= 2**32;
117              
118 250         4785 return __PACKAGE__->new_from_integer(
119             integer => $self->as_integer(),
120             version => 4,
121             )->as_string();
122             }
123              
124             sub as_bit_string {
125 6     6 1 2163 my $self = shift;
126              
127 6 100       18 if ( $self->version == 6 ) {
128 4         8 my $hex = uint128_to_hex( $self->as_integer() );
129 4         135 my @ha = $hex =~ /.{8}/g;
130 4         6 return join q{}, map { sprintf( '%032b', hex($_) ) } @ha;
  16         46  
131             }
132             else {
133 2         4 return sprintf( '%032b', $self->as_integer() );
134             }
135             }
136              
137 0     0 1 0 sub prefix_length { $_[0]->bits() }
138              
139 0     0 0 0 sub mask_length { $_[0]->prefix_length() }
140              
141             sub next_ip {
142 5     5 1 866 my $self = shift;
143              
144 5 100       7 confess "$self is the last address in its range"
145             if $self->as_integer() == $self->_max;
146              
147 3         5 return __PACKAGE__->new_from_integer(
148             integer => $self->as_integer() + 1,
149             version => $self->version(),
150             );
151             }
152              
153             sub previous_ip {
154 4     4 1 683 my $self = shift;
155              
156 4 100       5 confess "$self is the first address in its range"
157             if $self->as_integer() == 0;
158              
159 2         57 return __PACKAGE__->new_from_integer(
160             integer => $self->as_integer() - 1,
161             version => $self->version(),
162             );
163             }
164              
165             sub _compare_overload {
166 211     211   4796 my $self = shift;
167 211         171 my $other = shift;
168 211 50       243 my $flip = shift() ? -1 : 1;
169              
170             confess 'Cannot compare unless both objects are '
171             . __PACKAGE__
172             . ' objects'
173             unless blessed $self
174             && blessed $other
175 211 50 33     947 && eval { $self->isa(__PACKAGE__) && $other->isa(__PACKAGE__) };
  211 50 33     1070  
176              
177 211         327 return $flip * ( $self->as_integer() <=> $other->as_integer() );
178             }
179              
180             __PACKAGE__->meta()->make_immutable();
181              
182             1;
183              
184             # ABSTRACT: An object representing a single IP (4 or 6) address
185              
186             __END__