File Coverage

blib/lib/Exception/Sink.pm
Criterion Covered Total %
statement 9 72 12.5
branch 0 42 0.0
condition 0 3 0.0
subroutine 3 10 30.0
pod 7 7 100.0
total 19 134 14.1


line stmt bran cond sub pod time code
1             ##############################################################################
2             #
3             # Exception::Sink
4             # Copyright (c) 2006-2024 Vladi Belperchinov-Shabanski "Cade"
5             #
6             # http://cade.noxrun.com/
7             #
8             # GPLv2
9             #
10             ##############################################################################
11             #
12             # compact general purpose exception handling
13             #
14             ##############################################################################
15             package Exception::Sink;
16 1     1   146326 use Exporter;
  1         3  
  1         166  
17             our @ISA = qw( Exporter );
18             our @EXPORT = qw(
19             sink
20             dive
21             surface
22             surface2
23            
24             boom_skip
25             boom
26             );
27              
28             our @EXPORT_OK = qw(
29             $DEBUG_SINK
30             get_stack_trace
31             );
32            
33             our %EXPORT_TAGS = ( 'none' => [ ] );
34             our $VERSION = '3.09';
35 1     1   611 use Exception::Sink::Class;
  1         4  
  1         40  
36 1     1   7 use strict;
  1         2  
  1         1741  
37              
38             our $DEBUG_SINK = 0;
39              
40             sub sink($);
41             sub dive();
42             sub surface(@);
43             sub surface2(@);
44              
45             sub boom_skip($$);
46             sub boom($);
47              
48             ##############################################################################
49             #
50             # sink( "CLASS: ID: message..." )
51             # sinks the ship of class and id with reason message
52             #
53              
54             sub sink($)
55             {
56 0     0 1   my $msg = shift;
57              
58 0           my $org = $msg;
59              
60 0           my $class = 'UNKNOWN';
61 0           my $id = 'UNKNOWN';
62              
63 0 0 0       $class = $1 || $2 if $msg =~ s/^([a-z0-9_]+)\s*:\s*|^([a-z0-9_]+)$//i;
64 0 0         $id = $1 if $msg =~ s/^([a-z0-9_]+)\s*:\s*|^([a-z0-9_]+)$//i;
65              
66 0           $msg =~ s/\s+at\s+\/\S+.+$//;
67 0           chomp( $msg );
68              
69 0           my ( $p, $f, $l ) = caller();
70 0           $f =~ s/^(.*\/)([^\/]+)$/$2/;
71              
72 0           $class = uc $class;
73              
74 0 0         print STDERR "sink: $class ($f:$l)\n" if $DEBUG_SINK;
75              
76 0           die Exception::Sink::Class->new(
77             'CLASS' => $class,
78             'ID' => $id,
79             'MSG' => $msg,
80             'PACKAGE' => $p,
81             'FILE' => $f,
82             'LINE' => $l,
83             'ORG' => $org,
84             );
85             }
86              
87             ##############################################################################
88             #
89             # dive()
90             # continue sinking...
91             #
92              
93             sub dive()
94             {
95 0 0   0 1   print STDERR "dive: pre: $@\n" if $DEBUG_SINK;
96 0 0         return 0 unless $@;
97 0 0         if( !ref($@) )
98             {
99 0 0         print STDERR "dive: non-ship, resink: $@\n" if $DEBUG_SINK;
100             # re-sink, non-ship
101 0           my $AT=$@;
102 0           eval { sink "SINK: $AT"; }
  0            
103             };
104              
105 0 0         print STDERR "dive: $@->{CLASS}\n" if $DEBUG_SINK;
106              
107 0           die; # propagate
108             }
109              
110             ##############################################################################
111             #
112             # surface( class list )
113             # stops sinking of specific classes...
114             #
115              
116             sub surface(@)
117             {
118 0 0   0 1   print STDERR "surface: enter: $@ -> @_\n" if $DEBUG_SINK;
119 0 0         return 0 unless $@;
120 0 0         return 1 unless @_; # catch all
121 0 0         if( !ref($@) )
122             {
123 0 0         print STDERR "surface: non-ship, resink: $@\n" if $DEBUG_SINK;
124             # re-sink, non-ship
125 0           my $AT=$@;
126 0 0         if( $AT =~ /^[A-Z0-9_]+\:/ )
127             {
128 0           eval { sink $AT; }
  0            
129             }
130             else
131             {
132 0           eval { sink "SINK: $AT"; }
  0            
133             }
134             };
135              
136 0 0         print STDERR "surface: $@->{CLASS} -> @_?\n" if $DEBUG_SINK;
137              
138 0           for my $class ( @_ )
139             {
140 0 0         return 1 if $class eq '*';
141 0 0         return 1 if uc $class eq $@->{ 'CLASS' };
142             }
143 0 0         print STDERR "surface: $@->{CLASS} -> continuing...\n" if $DEBUG_SINK;
144 0           return 0;
145             }
146              
147             sub surface2(@)
148             {
149 0 0   0 1   return 1 if surface(@_);
150 0           dive();
151 0           return 0;
152             }
153              
154             ##############################################################################
155             #
156             # boom()
157             # sink with stack trace
158             #
159              
160             sub boom_skip($$)
161             {
162 0     0 1   my $msg = shift;
163 0           my $skip = shift;
164 0           chomp( $msg );
165 0           $msg = "BOOM: [$$] $msg\n";
166 0           sink( join '', ( $msg, get_stack_trace( $skip ) ) );
167             }
168              
169             sub boom($)
170             {
171 0     0 1   boom_skip($_[0],0);
172             }
173              
174             sub get_stack_trace
175             {
176 0     0 1   my $skip = shift;
177            
178 0           my @st;
179             my $i;
180 0           my $ml;
181            
182 0           $i = 1 + $skip; # skip get_stack_trace frame and optionally first N frames
183 0           while ( my ( $pack, $file, $line, $subname ) = caller($i++) )
184             {
185 0           my $l = length( "$pack::$subname" );
186 0 0         $ml = $l if $l > $ml;
187             }
188            
189 0           $i = 1 + $skip; # skip get_stack_trace frame and optionally first N frames
190 0           my $ii;
191 0           while ( my ( $pack, $file, $line, $subname ) = caller($i++) )
192             {
193 0           $ii++;
194 0           my $l = length( "$pack::$subname" );
195 0           my $pad = ' ' x ( $ml - $l );
196 0           push @st, " [$$] $ii: $pack::$subname $pad $file line $line\n";
197             }
198            
199 0 0         return wantarray ? ( @st ) : join( '', @st );
200             }
201              
202             ##############################################################################
203             1;
204             ##############################################################################
205              
206             __END__