File Coverage

blib/lib/Repl/Spec/Args/StdArgList.pm
Criterion Covered Total %
statement 59 59 100.0
branch 6 10 60.0
condition 10 19 52.6
subroutine 5 5 100.0
pod 0 2 0.0
total 80 95 84.2


line stmt bran cond sub pod time code
1             package Repl::Spec::Args::StdArgList;
2            
3 2     2   3353 use strict;
  2         6  
  2         76  
4 2     2   10 use warnings;
  2         4  
  2         134  
5 2     2   11 use Carp;
  2         2  
  2         1699  
6            
7             #
8             # There are 3 types of parameters in a standard argument list.
9             # 1. Fixed and required arguments. Each with its own type.
10             # 2. Optional. Each argument has its own type. These cannot be of type Pair, because this might conflict with the named arguments.
11             # The optional parameters can have a default value which will be used when the argument is not present.
12             # 3. Named (optional or required). These are pairs at the end of the command line.
13             # The named parameters can have a default value.
14             #
15            
16             sub new
17             {
18 2     2 0 22 my $invocant = shift;
19 2   33     11 my $class = ref($invocant) || $invocant;
20            
21 2   50     8 my $fixedArg = shift || die "Expected a arrayref containing fixed args.";
22 2   50     12 my $optArg = shift || die "Expected a arrayref containing optional args.";
23 2   50     6 my $namedArg = shift || die "Expected a arrayref containing named args.";
24            
25 2         4 my $self= {};
26 2         5 $self->{FIXED}=$fixedArg;
27 2         3 $self->{OPT}=$optArg;
28 2         5 $self->{NAMED}=$namedArg;
29            
30 2         63 return bless $self, $class;
31             }
32            
33            
34             # Parameters:
35             # - An argument list (ref to array).
36             # - A context! (which is not used by the type system, but it could be by the type specs).
37             # Notes:
38             # - The context is not used directly by the argument checker, it is passed to the
39             # type specs, so a type spec implementation could make use of it.
40             # - The argument list is expected to have the form ["cmd", arg1, ..., argn]
41             sub guard
42             {
43 4     4 0 10666 my $self = shift;
44            
45 4   50     25 my $args = shift || die "Argument list expected.";
46 4         9 my $argslen = scalar(@$args);
47 4   50     9 my $ctx = shift || die "Context expected";
48            
49 4         11 my $fixed = $self->{FIXED};
50 4         8 my $opt = $self->{OPT};
51 4         7 my $named = $self->{NAMED};
52            
53 4         8 my $newargs = [];
54 4         10 $newargs->[0] = $args->[0];
55            
56             # Argidx will be used for the different parameter types.
57 4         6 my $argidx = 1;
58            
59             # Test the fixed args.
60 4 50       15 croak sprintf("Too few arguments. Expected at least %d arguments but received %d.", scalar(@$fixed), ($argslen - 1)) if ((scalar($args) - 1) < scalar(@$fixed));
61 4         5 my $i = 0;
62 4         13 while ($i < scalar(@$fixed))
63             {
64 7         33 $newargs->[$argidx] = $fixed->[$i]->guard($args, $argidx, $ctx);
65 6         8 $i = $i + 1;
66 6         14 $argidx = $argidx + 1;
67             }
68            
69             # Test the optional args.
70 3         7 foreach my $spec (@$opt)
71             {
72 3         18 $newargs->[$argidx] = $spec->guard($args, $argidx, $ctx);
73 2         5 $argidx = $argidx + 1;
74             }
75            
76             # If there are still arguments left that are not pairs there are
77             # too many arguments.
78 2 50 66     12 croak sprintf("Too many arguments. Expected at most %d arguments.", scalar(@$fixed) + scalar(@$opt)) if($argidx < $argslen && !(ref($args->[$argidx]) eq "Repl::Core::Pair"));
79            
80             # Named args.
81 2         4 my $startnamed = $argslen - 1;
82 2   66     29 while($startnamed > 0 && (ref($args->[$startnamed]) eq "Repl::Core::Pair"))
83             {
84 1         5 $startnamed = $startnamed - 1;
85             }
86             # Now we can resolve the named arguments within this range.
87 2         4 foreach my $spec (@$named)
88             {
89 2         10 $newargs->[$argidx] = $spec->guard($args, $startnamed, $ctx);
90 2         5 $argidx = $argidx + 1;
91             }
92             # Finally we go looking for spurious named parameters that were not specified ...
93 2         4 my $j = $startnamed;
94 2         6 while ($j < $argslen)
95             {
96 3 100       9 if(ref($args->[$j]) eq "Repl::Core::Pair")
97             {
98 1         2 my $pair = $args->[$j];
99 1         3 my $left = $pair->getLeft();
100 1         3 my $right = $pair->getRight();
101 1         1 my $found = 0;
102            
103 1         8 foreach my $namedpar (@$named)
104             {
105 1 50       3 if($namedpar->name() eq $left)
106             {
107 1         2 $found = 1;
108 1         2 next;
109             }
110             }
111 1 50       4 croak sprintf("Found an unexpected named argument '%s'.", $left) if ! $found;
112             }
113 3         8 $j = $j + 1;
114             }
115            
116             # If we get here, we've done all the checking.
117 2         7 return $newargs;
118             }
119            
120             1;