line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Unexpected::Functions; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
148128
|
use strict; |
|
4
|
|
|
|
|
14
|
|
|
4
|
|
|
|
|
171
|
|
4
|
4
|
|
|
4
|
|
36
|
use warnings; |
|
4
|
|
|
|
|
12
|
|
|
4
|
|
|
|
|
191
|
|
5
|
4
|
|
|
4
|
|
1483
|
use parent 'Exporter::Tiny'; |
|
4
|
|
|
|
|
1447
|
|
|
4
|
|
|
|
|
29
|
|
6
|
|
|
|
|
|
|
|
7
|
4
|
|
|
4
|
|
18219
|
use Carp qw( croak ); |
|
4
|
|
|
|
|
12
|
|
|
4
|
|
|
|
|
408
|
|
8
|
4
|
|
|
4
|
|
442
|
use Package::Stash; |
|
4
|
|
|
|
|
5502
|
|
|
4
|
|
|
|
|
189
|
|
9
|
4
|
|
|
4
|
|
77
|
use Scalar::Util qw( blessed reftype ); |
|
4
|
|
|
|
|
21
|
|
|
4
|
|
|
|
|
318
|
|
10
|
4
|
|
|
4
|
|
1775
|
use Sub::Install qw( install_sub ); |
|
4
|
|
|
|
|
10702
|
|
|
4
|
|
|
|
|
26
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our @EXPORT_OK = qw( catch_class exception has_exception |
13
|
|
|
|
|
|
|
inflate_message inflate_placeholders is_class_loaded |
14
|
|
|
|
|
|
|
is_one_of_us parse_arg_list throw throw_on_error ); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
my $Exception_Class = 'Unexpected'; my $Should_Quote = 1; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# Private functions |
19
|
|
|
|
|
|
|
my $_catch = sub { |
20
|
|
|
|
|
|
|
my $block = shift; return ((bless \$block, 'Try::Tiny::Catch'), @_); |
21
|
|
|
|
|
|
|
}; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
my $_clone_one_of_us = sub { |
24
|
|
|
|
|
|
|
return $_[ 1 ] ? { %{ $_[ 0 ] }, %{ $_[ 1 ] } } : { error => $_[ 0 ] }; |
25
|
|
|
|
|
|
|
}; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
my $_dereference_code = sub { |
28
|
|
|
|
|
|
|
my ($code, @args) = @_; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
$args[ 0 ] and ref $args[ 0 ] eq 'ARRAY' and unshift @args, 'args'; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
return { class => $code->(), @args }; |
33
|
|
|
|
|
|
|
}; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
my $_exception_class_cache = {}; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
my $_exception_class = sub { |
38
|
|
|
|
|
|
|
my $caller = shift; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
exists $_exception_class_cache->{ $caller } |
41
|
|
|
|
|
|
|
and defined $_exception_class_cache->{ $caller } |
42
|
|
|
|
|
|
|
and return $_exception_class_cache->{ $caller }; |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
my $code = $caller->can( 'EXCEPTION_CLASS' ); |
45
|
|
|
|
|
|
|
my $class = $code ? $code->() : $Exception_Class; |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
return $_exception_class_cache->{ $caller } = $class; |
48
|
|
|
|
|
|
|
}; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
my $_match_class = sub { |
51
|
|
|
|
|
|
|
my ($e, $ref, $blessed, $does, $key) = @_; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
return !defined $key ? !defined $e |
54
|
|
|
|
|
|
|
: $key eq '*' ? 1 |
55
|
|
|
|
|
|
|
: $key eq ':str' ? !$ref |
56
|
|
|
|
|
|
|
: $key eq $ref ? 1 |
57
|
|
|
|
|
|
|
: $blessed && $e->can( 'instance_of' ) ? $e->instance_of( $key ) |
58
|
|
|
|
|
|
|
: $blessed && $e->$does( $key ) ? 1 |
59
|
|
|
|
|
|
|
: 0; |
60
|
|
|
|
|
|
|
}; |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
my $_quote_maybe = sub { |
63
|
|
|
|
|
|
|
return $Should_Quote ? "'".$_[ 0 ]."'" : $_[ 0 ]; |
64
|
|
|
|
|
|
|
}; |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
my $_gen_checker = sub { |
67
|
|
|
|
|
|
|
my @prototable = @_; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
return sub { |
70
|
|
|
|
|
|
|
my $e = shift; |
71
|
|
|
|
|
|
|
my $ref = ref $e; |
72
|
|
|
|
|
|
|
my $blessed = blessed $e; |
73
|
|
|
|
|
|
|
my $does = ($blessed && $e->can( 'DOES' )) || 'isa'; |
74
|
|
|
|
|
|
|
my @table = @prototable; |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
while (my ($key, $value) = splice @table, 0, 2) { |
77
|
|
|
|
|
|
|
$_match_class->( $e, $ref, $blessed, $does, $key ) and return $value |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
return; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
}; |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# Package methods |
85
|
|
|
|
|
|
|
sub import { |
86
|
23
|
|
|
23
|
|
291
|
my $class = shift; |
87
|
23
|
100
|
100
|
|
|
222
|
my $global_opts = { $_[ 0 ] && ref $_[ 0 ] eq 'HASH' ? %{+ shift } : () }; |
|
3
|
|
|
|
|
15
|
|
88
|
23
|
|
|
|
|
75
|
my $ex_class = delete $global_opts->{exception_class}; |
89
|
|
|
|
|
|
|
# uncoverable condition false |
90
|
23
|
|
66
|
|
|
165
|
my $target = $global_opts->{into} //= caller; |
91
|
23
|
|
|
|
|
84
|
my @want = @_; |
92
|
23
|
|
|
|
|
66
|
my @args = (); |
93
|
|
|
|
|
|
|
|
94
|
23
|
100
|
|
|
|
123
|
$ex_class or $ex_class = $_exception_class->( $target ); |
95
|
|
|
|
|
|
|
|
96
|
23
|
|
|
|
|
82
|
for my $sym (@want) { |
97
|
29
|
100
|
100
|
|
|
1281
|
if ($ex_class->can( 'is_exception' ) and $ex_class->is_exception( $sym )){ |
98
|
2
|
|
|
5
|
|
16
|
my $code = sub { sub { $sym } }; |
|
5
|
|
|
|
|
2009
|
|
|
5
|
|
|
|
|
33
|
|
99
|
|
|
|
|
|
|
|
100
|
2
|
|
|
|
|
21
|
install_sub { as => $sym, code => $code, into => $target, }; |
101
|
|
|
|
|
|
|
} |
102
|
27
|
|
|
|
|
110
|
else { push @args, $sym } |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
23
|
|
|
|
|
661
|
$class->SUPER::import( $global_opts, @args ); |
106
|
23
|
|
|
|
|
348806
|
return; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub quote_bind_values { # Deprecated. Use third arg in inflate_placeholders defs |
110
|
2
|
100
|
|
2
|
1
|
728
|
defined $_[ 1 ] and $Should_Quote = !!$_[ 1 ]; return $Should_Quote; |
|
2
|
|
|
|
|
8
|
|
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# Public functions |
114
|
|
|
|
|
|
|
sub parse_arg_list (;@) { # Coerce a hash ref from whatever was passed |
115
|
43
|
|
|
43
|
1
|
1311
|
my $n = 0; $n++ while (defined $_[ $n ]); |
|
43
|
|
|
|
|
287
|
|
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
return ( $n == 0) ? {} |
118
|
|
|
|
|
|
|
: (is_one_of_us( $_[ 0 ] )) ? $_clone_one_of_us->( @_ ) |
119
|
|
|
|
|
|
|
: ( ref $_[ 0 ] eq 'CODE') ? $_dereference_code->( @_ ) |
120
|
6
|
|
|
|
|
57
|
: ( ref $_[ 0 ] eq 'HASH') ? { %{ $_[ 0 ] } } |
121
|
|
|
|
|
|
|
: ( $n == 1) ? { error => $_[ 0 ] } |
122
|
|
|
|
|
|
|
: ( ref $_[ 1 ] eq 'ARRAY') ? { error => (shift), args => @_ } |
123
|
43
|
100
|
|
|
|
240
|
: ( ref $_[ 1 ] eq 'HASH') ? { error => $_[ 0 ], %{ $_[ 1 ] } } |
|
1
|
100
|
|
|
|
11
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
124
|
|
|
|
|
|
|
: ( $n % 2 == 1) ? { error => @_ } |
125
|
|
|
|
|
|
|
: { @_ }; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub catch_class ($@) { |
129
|
3
|
|
|
3
|
1
|
29
|
my $check = $_gen_checker->( @{+ shift }, '*' => sub { die $_[ 0 ] } ); |
|
11
|
|
|
11
|
|
10653
|
|
|
11
|
|
|
|
|
99
|
|
130
|
|
|
|
|
|
|
|
131
|
11
|
100
|
|
|
|
332
|
wantarray or croak 'Useless bare catch_class()'; |
132
|
|
|
|
|
|
|
|
133
|
10
|
|
100
|
10
|
|
68
|
return $_catch->( sub { ($check->( $_[ 0 ] ) || return)->( $_[ 0 ] ) }, @_ ); |
|
10
|
|
|
|
|
630
|
|
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub exception (;@) { |
137
|
1
|
|
|
1
|
1
|
9
|
return $_exception_class->( caller )->caught( @_ ); |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub has_exception ($;@) { |
141
|
5
|
|
|
5
|
1
|
1497
|
my ($name, %args) = @_; my $exception_class = caller; |
|
5
|
|
|
|
|
14
|
|
142
|
|
|
|
|
|
|
|
143
|
5
|
|
|
|
|
16
|
return $exception_class->add_exception( $name, \%args ); |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
sub inflate_message ($;@) { # Expand positional parameters of the form [_] |
147
|
31
|
|
|
31
|
1
|
133
|
return inflate_placeholders( [ '[?]', '[]' ], @_ ); |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub inflate_placeholders ($;@) { # Sub visible strings for null and undef |
151
|
46
|
|
|
46
|
1
|
108
|
my $defaults = shift; |
152
|
46
|
|
|
|
|
111
|
my $msg = shift; |
153
|
507
|
100
|
|
|
|
1095
|
my @vals = map { $defaults->[ 2 ] ? $_ : $_quote_maybe->( $_ ) } |
154
|
|
|
|
|
|
|
# uncoverable condition false |
155
|
507
|
100
|
|
|
|
1121
|
map { (length) ? $_ : $defaults->[ 1 ] } |
156
|
507
|
|
66
|
|
|
1134
|
map { $_ // $defaults->[ 0 ] } @_, |
157
|
46
|
|
|
|
|
145
|
map { $defaults->[ 0 ] } 0 .. 9; |
|
460
|
|
|
|
|
991
|
|
158
|
|
|
|
|
|
|
|
159
|
46
|
|
|
|
|
681
|
$msg =~ s{ \[ _ (\d+) \] }{$vals[ $1 - 1 ]}gmx; |
160
|
46
|
|
|
|
|
451
|
return $msg; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub is_class_loaded ($) { # Lifted from Class::Load |
164
|
17
|
|
|
17
|
1
|
1106
|
my $class = shift; my $stash = Package::Stash->new( $class ); |
|
17
|
|
|
|
|
157
|
|
165
|
|
|
|
|
|
|
|
166
|
17
|
100
|
|
|
|
182
|
if ($stash->has_symbol( '$VERSION' )) { |
167
|
5
|
|
|
|
|
10
|
my $version = ${ $stash->get_symbol( '$VERSION' ) }; |
|
5
|
|
|
|
|
26
|
|
168
|
|
|
|
|
|
|
|
169
|
5
|
100
|
|
|
|
15
|
if (defined $version) { |
170
|
4
|
100
|
|
|
|
19
|
not ref $version and return 1; |
171
|
|
|
|
|
|
|
# Sometimes $VERSION ends up as a reference to undef (weird) |
172
|
3
|
100
|
100
|
|
|
12
|
reftype $version eq 'SCALAR' and defined ${ $version } and return 1; |
|
2
|
|
|
|
|
11
|
|
173
|
2
|
100
|
|
|
|
11
|
blessed $version and return 1; # A version object |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
14
|
100
|
100
|
|
|
74
|
$stash->has_symbol( '@ISA' ) and @{ $stash->get_symbol( '@ISA' ) } |
|
3
|
|
|
|
|
36
|
|
178
|
|
|
|
|
|
|
and return 1; |
179
|
|
|
|
|
|
|
# Check for any method |
180
|
12
|
100
|
|
|
|
105
|
return $stash->list_all_symbols( 'CODE' ) ? 1 : 0; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub is_one_of_us ($) { |
184
|
71
|
|
100
|
71
|
1
|
1096
|
return $_[ 0 ] && (blessed $_[ 0 ]) && $_[ 0 ]->isa( $Exception_Class ); |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub throw (;@) { |
188
|
3
|
|
|
3
|
1
|
1995
|
$_exception_class->( caller )->throw( @_ ); |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
sub throw_on_error (;@) { |
192
|
2
|
|
|
2
|
1
|
44
|
return $_exception_class->( caller )->throw_on_error( @_ ); |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
1; |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
__END__ |