File Coverage

blib/lib/Zonemaster/Engine/DNSName.pm
Criterion Covered Total %
statement 57 59 96.6
branch 6 6 100.0
condition 5 5 100.0
subroutine 13 14 92.8
pod 7 7 100.0
total 88 91 96.7


line stmt bran cond sub pod time code
1             package Zonemaster::Engine::DNSName;
2              
3 26     26   89944 use version; our $VERSION = version->declare("v1.0.3");
  26         2298  
  26         167  
4              
5 26     26   2584 use 5.014002;
  26         108  
6 26     26   128 use warnings;
  26         54  
  26         679  
7              
8 26     26   588 use Moose;
  26         411047  
  26         150  
9 26     26   158590 use Moose::Util::TypeConstraints;
  26         97  
  26         240  
10              
11             coerce 'Zonemaster::Engine::DNSName', from 'Str', via { Zonemaster::Engine::DNSName->new( $_ ) };
12              
13             use overload
14 26         289 '""' => \&string,
15 26     26   56107 'cmp' => \&str_cmp;
  26         56  
16              
17             has 'labels' => ( is => 'ro', isa => 'ArrayRef[Str]', required => 1 );
18              
19             around BUILDARGS => sub {
20             my $orig = shift;
21             my $class = shift;
22              
23             if ( @_ == 1 && !ref $_[0] ) {
24             my $name = shift;
25             $name = q{} if not defined( $name );
26             my @labels = split( /[.]/x, $name );
27             return $class->$orig( labels => \@labels );
28             }
29             elsif ( ref( $_[0] ) and ref( $_[0] ) eq __PACKAGE__ ) {
30             return $_[0];
31             }
32             elsif ( ref( $_[0] ) and ref( $_[0] ) eq 'Zonemaster::Engine::Zone' ) {
33             return $_[0]->name;
34             }
35             else {
36             return $class->$orig( @_ );
37             }
38             };
39              
40             sub string {
41 3510697     3510697 1 4977365 my $self = shift;
42              
43 3510697         4365383 my $name = join( '.', @{ $self->labels } );
  3510697         84659293  
44 3510697 100       6475419 $name = '.' if $name eq q{};
45              
46 3510697         21736283 return $name;
47             }
48              
49             sub fqdn {
50 25     25 1 65 my ( $self ) = @_;
51              
52 25         42 return join( '.', @{ $self->labels } ) . '.';
  25         888  
53             }
54              
55             sub str_cmp {
56 634389     634389 1 1107484 my ( $self, $other ) = @_;
57 634389   100     1030624 $other //= q{}; # Treat undefined value as root
58              
59 634389         1129118 $other =~ s/(.+)[.]\z/$1/x;
60              
61 634389         1140163 return ( uc( "$self" ) cmp uc( $other ) );
62             }
63              
64             sub next_higher {
65 414     414 1 976 my $self = shift;
66 414         900 my @l = @{ $self->labels };
  414         10501  
67 414 100       1470 if ( @l ) {
68 412         820 shift @l;
69 412         10975 return Zonemaster::Engine::DNSName->new( labels => \@l );
70             }
71             else {
72 2         11 return;
73             }
74             }
75              
76             sub common {
77 12920     12920 1 40057 my ( $self, $other ) = @_;
78              
79 12920         26539 my @me = reverse @{ $self->labels };
  12920         339487  
80 12920         29157 my @them = reverse @{ $other->labels };
  12920         318091  
81              
82 12920         26458 my $count = 0;
83 12920   100     71235 while ( @me and @them ) {
84 51         96 my $m = shift @me;
85 51         78 my $t = shift @them;
86 51 100       135 if ( uc( $m ) eq uc( $t ) ) {
87 33         53 $count += 1;
88 33         98 next;
89             }
90             else {
91 18         34 last;
92             }
93             }
94              
95 12920         42420 return $count;
96             } ## end sub common
97              
98             sub prepend {
99 11     11 1 42 my ( $self, $label ) = @_;
100 11         26 my @labels = ( $label, @{ $self->labels } );
  11         299  
101              
102 11         319 return $self->new( { labels => \@labels } );
103             }
104              
105             sub TO_JSON {
106 0     0 1   my ( $self ) = @_;
107              
108 0           return $self->string;
109             }
110              
111             ## no critic (Modules::RequireExplicitInclusion)
112 26     26   16714 no Moose;
  26         55  
  26         143  
113             __PACKAGE__->meta->make_immutable;
114              
115             1;
116              
117             =head1 NAME
118              
119             Zonemaster::Engine::DNSName - class representing DNS names
120              
121             =head1 SYNOPSIS
122              
123             my $name1 = Zonemaster::Name->new('www.example.org');
124             my $name2 = Zonemaster::Name->new('ns.example.org');
125             say "Yay!" if $name1->common($name2) == 2;
126              
127             =head1 ATTRIBUTES
128              
129             =over
130              
131             =item labels
132              
133             A reference to a list of strings, being the labels the DNS name is made up from.
134              
135             =back
136              
137             =head1 METHODS
138              
139             =over
140              
141             =item new($input) _or_ new({ labels => \@labellist})
142              
143             The constructor can be called with either a single argument or with a reference
144             to a hash as in the example above.
145              
146             If there is a single argument, it must be either a non-reference, a
147             L<Zonemaster::Engine::DNSName> object or a L<Zonemaster::Engine::Zone> object.
148              
149             If it's a non-reference, it will be split at period characters (possibly after
150             stringification) and the resulting list used as the name's labels.
151              
152             If it's a L<Zonemaster::Engine::DNSName> object it will simply be returned.
153              
154             If it's a L<Zonemaster::Engine::Zone> object, the value of its C<name> attribute will
155             be returned.
156              
157             =item string()
158              
159             Returns a string representation of the name. The string representation is created by joining the labels with dots. If there are no labels, a
160             single dot is returned. The names created this way do not have a trailing dot.
161              
162             The stringification operator is overloaded to this function, so it should rarely be necessary to call it directly.
163              
164             =item fqdn()
165              
166             Returns the name as a string complete with a trailing dot.
167              
168             =item str_cmp($other)
169              
170             Overloads string comparison. Comparison is made after converting the names to upper case, and ignores any trailing dot on the other name.
171              
172             =item next_higher()
173              
174             Returns a new L<Zonemaster::Engine::DNSName> object, representing the name of the called one with the leftmost label removed.
175              
176             =item common($other)
177              
178             Returns the number of labels from the rightmost going left that are the same in both names. Used by the recursor to check for redirections going
179             up the DNS tree.
180              
181             =item prepend($label)
182              
183             Returns a new L<Zonemaster::Engine::DNSName> object, representing the called one with the given label prepended.
184              
185             =item TO_JSON
186              
187             Helper method for JSON encoding.
188              
189             =back
190              
191             =cut