File Coverage

blib/lib/Sub/Multi/Tiny/Dispatcher/TypeParams.pm
Criterion Covered Total %
statement 58 58 100.0
branch 8 10 80.0
condition 3 3 100.0
subroutine 13 13 100.0
pod 1 1 100.0
total 83 85 97.6


line stmt bran cond sub pod time code
1              
2             use 5.006;
3 5     5   1350 use strict;
  5         36  
4 5     5   31 use warnings;
  5         11  
  5         130  
5 5     5   25  
  5         10  
  5         143  
6             use parent 'Exporter';
7 5     5   670 use vars::i '@EXPORT' => qw(MakeDispatcher);
  5         407  
  5         42  
8 5     5   382  
  5         13  
  5         49  
9             use Guard;
10 5     5   2147 use Import::Into;
  5         1861  
  5         266  
11 5     5   31 use Sub::Multi::Tiny::Util qw(_hlog _line_mark_string _make_positional_copier
  5         12  
  5         152  
12 5         302 _complete_dispatcher);
13 5     5   28 use Type::Params qw(multisig);
  5         9  
14 5     5   2777 use Type::Tiny ();
  5         577394  
  5         64  
15 5     5   2221  
  5         15  
  5         2059  
16             our $VERSION = '0.000013';
17              
18             # Documentation {{{1
19              
20             =head1 NAME
21              
22             Sub::Multi::Tiny::Dispatcher::TypeParams - Dispatcher-maker using Type::Params for Sub::Multi::Tiny
23              
24             =head1 SYNOPSIS
25              
26             # In a multisub
27             require Sub::Multi::Tiny qw($param D:TypeParams);
28              
29             # Internals of Sub::Multi::Tiny
30             use Type::Params;
31             my $dispatcher_coderef =
32             Sub::Multi::Tiny::Dispatcher::TypeParams::MakeDispatcher({impls=>[]...});
33              
34             This module dispatches to any function that can be distinguished by the
35             C<multisig> function in L<Type::Params>. See
36             L<Type::Params/MULTIPLE SIGNATURES>.
37              
38             See L<Sub::Multi::Tiny> for more about the usage of this module.
39             This module does not export any symbols.
40              
41             =head1 USAGE NOTES
42              
43             =head2 Candidate order
44              
45             The candidates must be listed with more
46             specific first, since they are tried top to bottom. For example, constraint
47             L<Types::Standard/Str> matches any scalar (as of Types::Standard v1.004004), so
48             it should be listed after more specific constraints such as
49             L<Types::Standard/Int>.
50              
51             =head2 Named parameters
52              
53             C<Type::Parameters::multisig()> does not directly support named parameters.
54             Instead, use a slurpy hash (C<Dict>) parameter to collect named parameters.
55             An example is given in L<Type::Params/Mixed Positional and Named Parameters>.
56              
57             =head1 FUNCTIONS
58              
59             =cut
60              
61             # }}}1
62              
63             =head2 MakeDispatcher
64              
65             Make the default dispatcher for the given multi. See L</SYNOPSIS>.
66              
67             =cut
68              
69             # uniquify constraint names
70             my $_constraint_idx = 0;
71              
72             # Our own "any" type
73             my $_any_type = Type::Tiny->new(name => 'Any_SMTD_TypeParams');
74             # Default constraint accepts anything
75              
76             my $hr = shift; # Has possible_params and impls arrayrefs
77             my $code = '';
78 7     7 1 14 _hlog { require Data::Dumper;
79 7         16 "Making Type::Params dispatcher for: ",
80 7     7   44 Data::Dumper->Dump([$hr], ['multisub']) };
81 7         62  
82 7         50 # Make an array of typechecks for multisig()
83             my (@sigs, @impls, @copiers);
84             foreach my $impl (@{$hr->{impls}}) {
85 7         55 my @sig;
86 7         21 foreach my $param (@{$impl->{args}}) {
  7         26  
87 16         35  
88 16         30 # Sanity checks. TODO FIXME remove the need for these!
  16         47  
89             die "I don't yet know how to handle named arguments"
90             if $param->{named};
91             die "I don't yet know how to handle optional arguments"
92 17 50       55 if !$param->{reqd};
93              
94 17 50       55 # Make the constraint
95             my $constraint;
96             if($param->{type} && $param->{where}) {
97 17         31 $constraint = $param->{type} & $param->{where};
98 17 100 100     223 # Subtype - see http://blogs.perl.org/users/toby_inkster/2014/08/typetiny-tricks-1-quick-intersections.html
    100          
    100          
99 4         46 } elsif($param->{type}) {
100             $constraint = $param->{type};
101             } elsif($param->{where}) {
102 9         145 $constraint = Type::Tiny->new(
103             name => 'Constraint' . $_constraint_idx++ . '_' .
104             substr($param->{name}, 1),
105             constraint => $param->{where},
106             );
107             } else { # No constraint
108 3         34 $constraint = $_any_type;
109             }
110 1         3  
111             # Add it to the signature
112             push @sig, $constraint;
113             } #foreach param
114 17         4121  
115             push @sigs, [@sig];
116             push @impls, $impl->{code};
117 16         47  
118 16         39 # Use a straight positional copier. This is sufficient even for
119             # named parameters because Type::Params::multisig()
120             # fakes named parameters with a slurpy hash.
121             push @copiers, _make_positional_copier($hr->{defined_in}, $impl);
122             } #foreach impl
123 16         59  
124             my $checker = multisig(@sigs);
125              
126 7         48 # Make the dispatcher
127             $code .= _line_mark_string <<'EOT';
128             # Find the candidate
129 7         321444 @_ = $data[0]->(@_); # $checker. Dies on error.
130             # NOTE: this change can't be `local`ized because `goto`
131             # undoes the `local` - see #8
132             $candidate = $data[1]->[${^_TYPE_PARAMS_MULTISIG}]; # impls
133             $copier = $data[2]->[${^_TYPE_PARAMS_MULTISIG}]; # copiers
134             EOT
135              
136             return _complete_dispatcher($hr, $code, $checker, \@impls, \@copiers);
137             } #MakeDispatcher
138 7         43  
139             =head2 import
140              
141             When used, also imports L<Type::Tiny> into the caller's namespace (since
142             C<Type::Tiny> types are how this dispatcher functions!).
143             The caller may also wish to import L<Types::Standard>, but we don't do so
144             here in the interest of generality.
145              
146             =cut
147              
148             my $target = caller;
149             __PACKAGE__->export_to_level(1, @_);
150             Type::Tiny->import::into($target);
151 8     8   1455 }
152 8         739  
153 8         68 1;
154              
155             # Rest of documentation {{{1
156              
157             =head1 AUTHOR
158              
159             Chris White E<lt>cxw@cpan.orgE<gt>
160              
161             =head1 LICENSE
162              
163             Copyright (C) 2019 Chris White E<lt>cxw@cpan.orgE<gt>
164              
165             This library is free software; you can redistribute it and/or modify
166             it under the same terms as Perl itself.
167              
168             =cut
169              
170             # }}}1
171             # vi: set fdm=marker: #