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   138764 use strict;
  4         4  
  4         108  
4 4     4   14 use warnings;
  4         4  
  4         117  
5              
6             our $VERSION = '0.21';
7              
8 4     4   13 use Carp qw( confess );
  4         4  
  4         179  
9 4     4   16 use Math::Int128 0.06 qw( uint128 uint128_to_hex uint128_to_number );
  4         56  
  4         173  
10 4     4   1018 use Net::Works::Types qw( PackedBinary Str );
  4         7  
  4         202  
11 4         221 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   1161 );
  4         5  
17 4     4   17 use Scalar::Util qw( blessed );
  4         4  
  4         143  
18 4     4   14 use Socket 1.99 qw( AF_INET AF_INET6 inet_pton inet_ntop );
  4         60  
  4         157  
19              
20 4     4   1186 use integer;
  4         22  
  4         15  
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   151 );
  4         5  
32              
33 4     4   9202 use Moo;
  4         10211  
  4         66  
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 106641 my $self = shift;
55              
56 2525         4779 $self->_validate_ip_integer();
57              
58 2523         32564 return;
59             }
60              
61             sub new_from_string {
62 127     127 1 22022 my $class = shift;
63 127         237 my %p = @_;
64              
65 127         167 my $str = delete $p{string};
66 127         146 my $version = delete $p{version};
67              
68 127 100 100     558 if ( defined $str && inet_pton( AF_INET, $str ) ) {
69 43   100     84 $version ||= 4;
70 43 100       81 $str = '::' . $str if $version == 6;
71             }
72             else {
73 84   100     146 $version ||= 6;
74 84         155 _validate_ip_string( $str, $version );
75             }
76              
77 110         202 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 17110 my $class = shift;
86 2425         4253 my %p = @_;
87              
88 2425         3143 my $int = delete $p{integer};
89 2425         2258 my $version = delete $p{version};
90 2425 0 33     3929 $version ||= ref $int ? 6 : 4;
91              
92 2425         36904 return $class->new(
93             _integer => $int,
94             version => $version,
95             %p,
96             );
97             }
98              
99             sub _build_string {
100 2008     2008   153264 my $self = shift;
101              
102 2008         4596 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 18777 sub as_integer { $_[0]->_integer() }
108              
109             sub as_ipv4_string {
110 657     657 1 20453 my $self = shift;
111              
112 657 100       2013 return $self->as_string() if $self->version() == 4;
113              
114 623 100       1003 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         5162 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 2224 my $self = shift;
126              
127 6 100       20 if ( $self->version == 6 ) {
128 4         9 my $hex = uint128_to_hex( $self->as_integer() );
129 4         142 my @ha = $hex =~ /.{8}/g;
130 4         31 return join q{}, map { sprintf( '%032b', hex($_) ) } @ha;
  16         49  
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 1088 my $self = shift;
143              
144 5 100       9 confess "$self is the last address in its range"
145             if $self->as_integer() == $self->_max;
146              
147 3         7 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 782 my $self = shift;
155              
156 4 100       36 confess "$self is the first address in its range"
157             if $self->as_integer() == 0;
158              
159 2         62 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   4569 my $self = shift;
167 211         202 my $other = shift;
168 211 50       218 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     862 && eval { $self->isa(__PACKAGE__) && $other->isa(__PACKAGE__) };
  211 50 33     959  
176              
177 211         268 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__