File Coverage

blib/lib/Net/FreeIPA/Error.pm
Criterion Covered Total %
statement 74 74 100.0
branch 30 30 100.0
condition 17 21 80.9
subroutine 14 14 100.0
pod 6 6 100.0
total 141 145 97.2


line stmt bran cond sub pod time code
1             package Net::FreeIPA::Error;
2             $Net::FreeIPA::Error::VERSION = '3.0.0';
3 9     9   393 use strict;
  9         10  
  9         197  
4 9     9   26 use warnings;
  9         11  
  9         208  
5              
6 9     9   26 use base qw(Exporter);
  9         9  
  9         672  
7              
8             our @EXPORT = qw(mkerror);
9              
10 9     9   822 use Readonly;
  9         4120  
  9         399  
11              
12 9     9   1729 use overload bool => 'is_error', '==' => '_is_equal', '!=' => '_is_not_equal', '""' => '_stringify';
  9         1384  
  9         39  
13              
14             Readonly our $DUPLICATE_ENTRY => 'DuplicateEntry';
15             Readonly our $NOT_FOUND => 'NotFound';
16              
17             Readonly::Hash our %ERROR_CODES => {
18             $DUPLICATE_ENTRY => 4002,
19             $NOT_FOUND => 4001,
20             };
21              
22             Readonly::Hash our %REVERSE_ERROR_CODES => map {$ERROR_CODES{$_} => $_} keys %ERROR_CODES;
23              
24             =head1 NAME
25              
26             Net::FreeIPA::Error is an error class for Net::FreeIPA.
27              
28             Boolean logic and (non)-equal operator are overloaded using C method.
29             (Use C<==> and C also for name/message, not C / C operators).
30              
31             =head2 Public methods
32              
33             =over
34              
35             =item mkerror
36              
37             A C factory
38              
39             =cut
40              
41             sub mkerror
42             {
43 43     43 1 6796 return Net::FreeIPA::Error->new(@_);
44             }
45              
46              
47             =item new
48              
49             Create new error instance from options, e.g. from a (decoded dereferenced) JSON response.
50              
51             Arguments are handled by C.
52              
53             =cut
54              
55             sub new
56             {
57 44     44 1 782 my $this = shift;
58 44   33     145 my $class = ref($this) || $this;
59 44         71 my $self = {
60             __errattr => [],
61             };
62 44         48 bless $self, $class;
63              
64 44         60 $self->set_error(@_);
65              
66 44         77 return $self;
67             };
68              
69             =item set_error
70              
71             Process arguments to error
72              
73             =over
74              
75             =item no args/undef: reset the error attribute
76              
77             =item single argument string: convert to an C instance with message
78              
79             =item single argument hasref/Error instance: make a copy
80              
81             =item single argument/other: set Error message and save original in _orig attribute
82              
83             =item options (more than one arg): set the options
84              
85             =back
86              
87             =cut
88              
89             sub set_error
90             {
91 51     51 1 58 my $self = shift;
92              
93 51         43 my $nrargs = scalar @_;
94              
95 51         37 my %opts;
96 51 100       111 if ($nrargs == 1) {
    100          
97 10         11 my $err = shift;
98 10         9 my $ref = ref($err);
99              
100 10 100 100     46 if($ref eq 'Net::FreeIPA::Error') {
    100          
    100          
    100          
101 1         1 %opts = map {$_ => $err->{$_}} @{$err->{__errattr}};
  1         3  
  1         3  
102             } elsif ($ref eq 'HASH') {
103 2         9 %opts = %$err;
104             } elsif (defined($err) && $ref eq '') {
105 3         5 $opts{message} = $err;
106             } elsif (defined($err)) {
107 1         3 $opts{message} = "unknown error type $ref, see _orig attribute";
108 1         2 $opts{_orig} = $err;
109             }
110             } elsif ($nrargs > 1) {
111 31         51 %opts = @_;
112             }
113              
114              
115             # Wipe current state
116             # Do this after the %opts are build, to allow copies of itself
117 51         46 foreach my $key (@{$self->{__errattr}}) {
  51         148  
118 7         15 delete $self->{$key};
119             }
120 51         59 $self->{__errattr} = [];
121              
122             # sort produces sorted __errattr
123 51         102 foreach my $key (sort keys %opts) {
124 40         40 $self->{$key} = $opts{$key};
125 40         23 push(@{$self->{__errattr}}, $key);
  40         66  
126             }
127              
128 51         69 return $self;
129             }
130              
131             =item is_error
132              
133             Test if this is an error or not.
134              
135             If an optiona l C argument is passed,
136             test if error name or code is equal to C.
137              
138             A numerical type is compare to the code, a string is compare to the name or message
139              
140             For a set of known errorcodes, a automatic reverse lookup is performed.
141             When e.g. only the error name attribute is set, one can test using a known errorcode.
142              
143             =cut
144              
145             sub is_error
146             {
147 130     130 1 174 my ($self, $type, $reverse_lookup) = @_;
148              
149 130 100       201 $reverse_lookup = 1 if ! defined($reverse_lookup);
150              
151 130         83 my $res;
152              
153 130 100       132 if(defined($type)) {
154 46         27 my $revtype;
155              
156 46 100       144 if ($type =~ m/^\d+$/) {
157 25 100       78 $revtype = $REVERSE_ERROR_CODES{$type} if (exists($REVERSE_ERROR_CODES{$type}));
158 25   100     193 $res = exists($self->{code}) && ($self->{code} == $type);
159             } else {
160 21 100       53 $revtype = $ERROR_CODES{$type} if (exists($ERROR_CODES{$type}));
161 21   66     218 $res = (exists($self->{name}) && ($self->{name} eq $type)) || (exists($self->{message}) && ($self->{message} eq $type));
162             }
163              
164             # If a reverse known error is found, and it is not yet an error, lookup the reverse
165             # Disable the reverse-lookup here to avoid loop
166 46 100 100     160 $res = $self->is_error($revtype, 0) if ($reverse_lookup && defined($revtype) && ! $res);
      100        
167             } else {
168 84   66     192 $res = exists($self->{code}) || exists($self->{name}) || exists($self->{message});
169             }
170              
171 130 100       303 return $res ? 1 : 0;
172             }
173              
174             =item is_duplicate
175              
176             Test if this is a DuplicateEntry error
177              
178             =cut
179              
180             sub is_duplicate
181             {
182 4     4 1 13 my ($self) = @_;
183              
184 4         6 return $self->is_error($DUPLICATE_ENTRY);
185             }
186              
187             =item is_not_found
188              
189             Test if this is a NotFound error
190              
191             =cut
192              
193             sub is_not_found
194             {
195 4     4 1 14 my ($self) = @_;
196              
197 4         6 return $self->is_error($NOT_FOUND);
198             }
199              
200             # is_equal for overloading ==
201             sub _is_equal
202             {
203             # Use shift, looks like a 3rd argument (an empty string) is passed
204 7     7   298 my $self = shift;
205 7         9 return $self->is_error(shift);
206             }
207              
208             # inverse is_equal for overloading !=
209             sub _is_not_equal
210             {
211 1     1   2 my $self = shift;
212 1         2 return ! $self->_is_equal(@_);
213             }
214              
215             # _stringify create string for stringification
216             sub _stringify
217             {
218 38     38   605 my $self = shift;
219              
220 38 100       44 if ($self->is_error()) {
221 27         29 my @fields;
222 27         28 foreach my $attr (qw(name code message)) {
223 81 100       125 push(@fields, $self->{$attr}) if exists ($self->{$attr});
224             }
225 27         108 return "Error ".join('/', @fields);
226             } else {
227 11         40 return "No error";
228             };
229             }
230              
231             =pod
232              
233             =back
234              
235             =cut
236              
237             1;