File Coverage

lib/NetObj/MacAddress.pm
Criterion Covered Total %
statement 64 64 100.0
branch 16 16 100.0
condition 2 2 100.0
subroutine 20 20 100.0
pod 6 7 85.7
total 108 109 99.0


line stmt bran cond sub pod time code
1 8     8   121028 use strict;
  8         14  
  8         266  
2 8     8   27 use warnings FATAL => 'all';
  8         11  
  8         268  
3 8     8   135 use 5.014;
  8         19  
  8         325  
4             package NetObj::MacAddress;
5             $NetObj::MacAddress::VERSION = '1.0';
6             # ABSTRACT: represent a MAC address
7              
8 8     8   3263 use Moo;
  8         77487  
  8         30  
9 8     8   7801 use Carp;
  8         14  
  8         1108  
10              
11             sub _to_binary {
12             my ($macaddr) = @_;
13              
14             $macaddr =~ s{[-:\.]}{}xmsgi;
15             return unless $macaddr =~ m{\A [\d a-f]{12} \Z}xmsi;
16              
17             return pack('H2' x 6, unpack('A2' x 6, $macaddr));
18             }
19              
20 8     8   2920 use namespace::clean;
  8         58658  
  8         33  
21              
22             sub is_valid {
23 8     8 1 1159 my ($macaddr) = @_;
24 8 100       33 croak 'NetObj::MacAddress::is_valid is a class method only'
25             if ref($macaddr) eq __PACKAGE__;
26              
27 7         9 return !! _to_binary($macaddr);
28             }
29              
30             has binary => (
31             is => 'ro',
32             );
33              
34             sub BUILDARGS {
35 35     35 0 13137 my ($class, $mac, @args) = @_;
36 35 100       80 croak 'no MAC address given' unless defined($mac);
37 34 100       65 croak 'too many arguments in constructor for ' . __PACKAGE__ if @args;
38              
39 33 100       107 return { binary => $mac->binary() } if ref($mac) eq __PACKAGE__;
40              
41 31 100       81 $mac = _to_binary($mac) unless length($mac) == 6;
42 31 100       85 croak 'invalid MAC address' unless $mac;
43 28         378 return { binary => $mac };
44             }
45              
46 8     8   3836 use NetObj::MacAddress::Formatter::Base16;
  8         12  
  8         932  
47             sub to_string {
48 47     47 1 176 my ($self, $format) = @_;
49 47   100     120 $format //= 'base16';
50 47         66 $format = lc($format);
51              
52 47         44 state $formatter = {};
53              
54 47 100       65 if (not exists($formatter->{$format})) {
55 9         16 my $pkg = ucfirst($format);
56 9         18 my $sub = "NetObj::MacAddress::Formatter::${pkg}::format";
57 9 100       31 if (defined(&$sub)) {
58 8         25 $formatter->{$format} = \&$sub;
59             }
60             else {
61 1         21 croak "no formatter for $format";
62             }
63             }
64 46         107 return $formatter->{$format}($self);
65             }
66              
67 8     8   6556 use overload q("") => sub {shift->to_string};
  8     39   5361  
  8         46  
  39         54  
68              
69             use overload q(<=>) => sub {
70 3     3   728 my ($x, $y) = @_;
71 3         28 return $x->binary() cmp $y->binary()
72 8     8   572 };
  8         12  
  8         31  
73             use overload q(cmp) => sub {
74 3     3   666 my ($x, $y) = @_;
75 3         4 return "$x" cmp "$y"
76 8     8   544 };
  8         8  
  8         43  
77              
78              
79             # NOTE: vec(EXPR, OFFSET, BITS) treats EXPR as little endian on all platforms
80             # see: perldoc -f vec
81              
82             sub is_unicast {
83 16     16 1 100 my ($self) = @_;
84 16         55 return not vec($self->binary(), 0, 1);
85             }
86              
87             sub is_multicast {
88 8     8 1 10 my ($self) = @_;
89 8         7 return not $self->is_unicast();
90             }
91              
92             sub is_global {
93 16     16 1 96 my ($self) = @_;
94 16         59 return not vec($self->binary(), 1, 1);
95             }
96              
97             sub is_local {
98 8     8 1 9 my ($self) = @_;
99 8         12 return not $self->is_global();
100             }
101              
102             1;
103              
104             __END__
105              
106             =pod
107              
108             =encoding UTF-8
109              
110             =head1 NAME
111              
112             NetObj::MacAddress - represent a MAC address
113              
114             =head1 VERSION
115              
116             version 1.0
117              
118             =head1 SYNOPSIS
119              
120             use NetObj::MacAddress;
121              
122             # construct, supports various typical notations
123             my $mac1 = NetObj::MacAddress->new('08:00:20:1e:bc:78');
124             my $mac2 = NetObj::MacAddress->new('08-00-20-1E-BC-78');
125             my $mac3 = NetObj::MacAddress->new('6c40.087c.5e90');
126              
127             # comparison, numerically
128             $mac1 == $mac2; # true
129             $mac1 != $mac2; # false
130             $mac1 == $mac3; # false
131             $mac1 != $mac3; # true
132              
133             # comparison, stringwise
134             $mac1 eq $mac2; # true
135             $mac1 ne $mac2; # false
136             $mac1 eq $mac3; # false
137             $mac1 ne $mac3; # true
138              
139             # reject invalid MAC addresses
140             my $invalid_mac = NetObj::MacAddress->new('foo'); # throws exception
141              
142             # test for validity
143             NetObj::MacAddress::is_valid('08:00:20:1e:bc:78'); # true
144             NetObj::MacAddress::is_valid('foo'); # false
145              
146             # allow raw binary MAC addresses (any combination of 6 bytes)
147             my $mac4 = NetObj::MacAddress->new('l@,foo');
148             # represent as hex (base16)
149             $mac4->to_string(); # '6c402c666f6f'
150             # or as the raw binary
151             $mac4->binary(); # 'l@,foo'
152              
153             =head1 DESCRIPTION
154              
155             NetObj::MacAddress represents MAC addresses. The constructor makes sure that
156             only valid MAC addresses can be instantiated. Two MAC addresses compare equal
157             if they represent the same address independently of the notation used in the
158             constructor.
159              
160             NetObj::MacAddress is implemented as a Moose style object class (using Moo).
161              
162             =head1 METHODS
163              
164             =head2 is_valid
165              
166             The class method C<NetObj::MacAddress::is_valid> tests for the validity of a MAC address represented by a string. It does not throw an exception but returns false for an invalid and true for a valid MAC address.
167              
168             =head2 new
169              
170             The constructor expects exactly one argument either as a raw 6 byte value or a
171             string representation in a typically notation of hex characters. It throws an
172             exception for invalid MAC addresses.
173              
174             =head2 binary
175              
176             The C<binary> method returns the raw 6 bytes of the MAC address.
177              
178             =head2 to_string
179              
180             The C<to_string> method returns the MAC address in hex notation (base16). Optionally, if it is given the name of a formatter it will format the string in the corresponding style. The default style is called C<'base16'>.
181              
182             my $mac = NetObj::MacAddress->new('0800201ebc78');
183              
184             $mac->to_string(); # '0800201ebc78'
185             $mac->to_string('base16'); # '0800201ebc78'
186              
187             use NetObj::MacAddress::Formatter::Colons;
188             $mac->to_string('colons'); # '08:00:20:1e:bc:78'
189              
190             use NetObj::MacAddress::Formatter::Dashes;
191             $mac->to_string('dashes'); # '08-00-20-1E-BC-78'
192              
193             use NetObj::MacAddress::Formatter::Dots;
194             $mac->to_string('dots'); # '0800.201e.bc78'
195              
196             Some formatters are available by default (see examples above), others can be
197             added if needed by providing a module with a package name beginning with
198             C<NetObj::MacAddress::Formatter::> similarly to the existing ones.
199              
200             =head2 is_multicast
201             =method is_unicast
202              
203             The methods C<is_multicast> and C<is_unicast> indicate whether a MAC address is
204             multicast or unicast, respectively.
205              
206             my $unicast_mac = NetObj::MacAddress->new('000001abcdef');
207             my $multicast_mac = NetObj::MacAddress->new('010001abcdef');
208             $unicast_mac->is_unicast(); # true
209             $unicast_mac->is_multicast(); # false
210             $multicast_mac->is_unicast(); # false
211             $multicast_mac->is_multicast(); # true
212              
213             =head2 is_global
214             =method is_local
215              
216             The methods C<is_global> and C<is_local> indicate whether a MAC address is
217             globally or locally assigned, respectively.
218              
219             my $local_mac = NetObj::MacAddress->new('000001abcdef');
220             my $global_mac = NetObj::MacAddress->new('020001abcdef');
221             $local_mac->is_local(); # true
222             $local_mac->is_global(); # false
223             $global_mac->is_local(); # false
224             $global_mac->is_global(); # true
225              
226             =for Pod::Coverage BUILDARGS
227              
228             =head1 AUTHOR
229              
230             Elmar S. Heeb <elmar@heebs.ch>
231              
232             =head1 COPYRIGHT AND LICENSE
233              
234             This software is Copyright (c) 2015 by Elmar S. Heeb.
235              
236             This is free software, licensed under:
237              
238             The GNU General Public License, Version 3, June 2007
239              
240             =cut