File Coverage

blib/lib/Net/OpenStack/Client/Error.pm
Criterion Covered Total %
statement 71 71 100.0
branch 27 30 90.0
condition 17 24 70.8
subroutine 12 12 100.0
pod 4 4 100.0
total 131 141 92.9


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