File Coverage

blib/lib/Return/Type.pm
Criterion Covered Total %
statement 78 84 92.8
branch 13 20 65.0
condition 7 14 50.0
subroutine 15 15 100.0
pod 1 2 50.0
total 114 135 84.4


line stmt bran cond sub pod time code
1 2     2   28990 use 5.008;
  2         8  
  2         74  
2 2     2   11 use strict;
  2         4  
  2         65  
3 2     2   19 use warnings;
  2         3  
  2         188  
4              
5             package Return::Type;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.004';
9              
10 2     2   17344 use Attribute::Handlers;
  2         21841  
  2         13  
11 2     2   831 use Eval::TypeTiny qw( eval_closure );
  2         1065  
  2         18  
12 2     2   4913 use Sub::Identify qw( sub_fullname );
  2         2334  
  2         126  
13 2     2   13 use Sub::Name qw( subname );
  2         2  
  2         84  
14 2     2   838 use Types::Standard qw( Any ArrayRef HashRef Int );
  2         76820  
  2         31  
15 2     2   2166 use Types::TypeTiny qw( to_TypeTiny );
  2         4  
  2         14  
16              
17             sub _inline_type_check
18             {
19 12     12   19 my $class = shift;
20 12         26 my ($type, $var, $env, $suffix) = @_;
21            
22 12 50       37 return $type->inline_assert($var) if $type->can_be_inlined;
23            
24 0         0 $env->{'$type_'.$suffix} = \$type;
25 0         0 return sprintf('$type_%s->assert_return(%s);', $suffix, $var);
26             }
27              
28             sub _inline_type_coerce_and_check
29             {
30 4     4   8 my $class = shift;
31 4         10 my ($type, $var, $env, $suffix) = @_;
32            
33 4         8 my $coerce = '';
34 4 50 33     15 if ($type->has_coercion and $type->coercion->can_be_inlined)
    0          
35             {
36 4         467 $coerce = sprintf('%s = %s;', $var, $type->coercion->inline_coercion($var));
37             }
38             elsif ($type->has_coercion)
39             {
40 0         0 $env->{'$coercion_'.$suffix} = \( $type->coercion );
41 0         0 $coerce = sprintf('%s = $coercion_%s->coerce(%s);', $var, $suffix, $var);
42             }
43            
44 4         2237 return $coerce . $class->_inline_type_check(@_);
45             }
46              
47             sub wrap_sub
48             {
49 6     6 1 111993 my $class = shift;
50 6         13 my $sub = $_[0];
51 6         43 local %_ = @_[ 1 .. $#_ ];
52            
53 6   66     133 $_{$_} &&= to_TypeTiny($_{$_}) for qw( list scalar );
54 6   33     236 $_{scalar} ||= Any;
55 6 100 66     71 $_{list} ||= ($_{scalar} == Any ? Any : ArrayRef[$_{scalar}]);
56            
57 6         20584 my $prototype = prototype($sub);
58 6 50       25 $prototype = defined($prototype) ? "($prototype)" : "";
59            
60 6         23 my %env = ( '$sub' => \$sub );
61 6         39 my @src = sprintf('sub %s { my $wa = wantarray;', $prototype);
62 6         14 my $call = '$sub->(@_)';
63            
64 6 50       23 if ($_{scope_upper})
65             {
66 0         0 require Scope::Upper;
67 0         0 $call = '&Scope::Upper::uplevel($sub => (@_) => &Scope::Upper::SUB(&Scope::Upper::SUB))';
68             }
69            
70 6   50     58 exists($_{$_}) || ($_{$_} = $_{coerce}) for qw( coerce_list coerce_scalar );
71 6 100       24 my $inline_list = $_{coerce_list} ? '_inline_type_coerce_and_check' : '_inline_type_check';
72 6 100       19 my $inline_scalar = $_{coerce_scalar} ? '_inline_type_coerce_and_check' : '_inline_type_check';
73            
74             # List context
75 6         101 push @src, 'if ($wa) {';
76 6 100       35 if ( $_{list}->is_a_type_of(HashRef) )
77             {
78 1         115 push @src, 'my $rv = do { use warnings FATAL => qw(misc); +{' . $call . '} };';
79 1         31 push @src, $class->$inline_list($_{list}, '$rv', \%env, 'l');
80 1         170 push @src, 'return %$rv;';
81             }
82             else
83             {
84 5         4576 push @src, 'my $rv = [' . $call . '];';
85 5         43 push @src, $class->$inline_list($_{list}, '$rv', \%env, 'l');
86 5         446 push @src, 'return @$rv;';
87             }
88 6         37 push @src, '}';
89            
90             # Scalar context
91 6         14 push @src, 'elsif (defined $wa) {';
92 6         15 push @src, 'my $rv = ' . $call . ';';
93 6         25 push @src, $class->$inline_scalar($_{scalar}, '$rv', \%env, 's');
94 6         486 push @src, 'return $rv;';
95 6         11 push @src, '}';
96            
97             # Void context - cannot request a value to check, so check must be skipped
98 6         15 push @src, "$call;";
99            
100 6         12 push @src, '}';
101            
102 6         38 my $rv = eval_closure(
103             source => \@src,
104             environment => \%env,
105             );
106 6         4259 return subname(sub_fullname($sub), $rv);
107             }
108              
109             sub UNIVERSAL::ReturnType :ATTR(CODE)
110             {
111 1     1 0 3773 my ($package, $symbol, $referent, $attr, $data) = @_;
112            
113 2     2   1759 no warnings qw(redefine);
  2         4  
  2         172  
114 1 50       10 my %args = (@$data % 2) ? (scalar => @$data) : @$data;
115 1         8 *$symbol = __PACKAGE__->wrap_sub($referent, %args);
116 2     2   9 }
  2         4  
  2         16  
117              
118             1;
119              
120             __END__