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   48389 use version; our $VERSION = version->declare("v1.0.3");
  26         1407  
  26         163  
4              
5 26     26   2506 use 5.014002;
  26         96  
6 26     26   123 use warnings;
  26         52  
  26         642  
7              
8 26     26   421 use Moose;
  26         378651  
  26         150  
9 26     26   148922 use Moose::Util::TypeConstraints;
  26         78  
  26         189  
10              
11             coerce 'Zonemaster::Engine::DNSName', from 'Str', via { Zonemaster::Engine::DNSName->new( $_ ) };
12              
13             use overload
14 26         277 '""' => \&string,
15 26     26   50313 'cmp' => \&str_cmp;
  26         66  
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 3510889     3510889 1 5092472 my $self = shift;
42              
43 3510889         4387147 my $name = join( '.', @{ $self->labels } );
  3510889         86555854  
44 3510889 100       6656163 $name = '.' if $name eq q{};
45              
46 3510889         22049304 return $name;
47             }
48              
49             sub fqdn {
50 25     25 1 58 my ( $self ) = @_;
51              
52 25         50 return join( '.', @{ $self->labels } ) . '.';
  25         872  
53             }
54              
55             sub str_cmp {
56 634437     634437 1 1129778 my ( $self, $other ) = @_;
57 634437   100     1038886 $other //= q{}; # Treat undefined value as root
58              
59 634437         1180078 $other =~ s/(.+)[.]\z/$1/x;
60              
61 634437         1171920 return ( uc( "$self" ) cmp uc( $other ) );
62             }
63              
64             sub next_higher {
65 414     414 1 940 my $self = shift;
66 414         854 my @l = @{ $self->labels };
  414         10723  
67 414 100       1489 if ( @l ) {
68 412         917 shift @l;
69 412         11182 return Zonemaster::Engine::DNSName->new( labels => \@l );
70             }
71             else {
72 2         8 return;
73             }
74             }
75              
76             sub common {
77 12920     12920 1 41803 my ( $self, $other ) = @_;
78              
79 12920         28091 my @me = reverse @{ $self->labels };
  12920         348044  
80 12920         28003 my @them = reverse @{ $other->labels };
  12920         320094  
81              
82 12920         27197 my $count = 0;
83 12920   100     74171 while ( @me and @them ) {
84 51         89 my $m = shift @me;
85 51         84 my $t = shift @them;
86 51 100       121 if ( uc( $m ) eq uc( $t ) ) {
87 33         53 $count += 1;
88 33         91 next;
89             }
90             else {
91 18         34 last;
92             }
93             }
94              
95 12920         43611 return $count;
96             } ## end sub common
97              
98             sub prepend {
99 11     11 1 65 my ( $self, $label ) = @_;
100 11         27 my @labels = ( $label, @{ $self->labels } );
  11         277  
101              
102 11         289 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   15392 no Moose;
  26         55  
  26         176  
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