File Coverage

blib/lib/Getopt/EX/Numbers.pm
Criterion Covered Total %
statement 72 77 93.5
branch 28 36 77.7
condition 14 20 70.0
subroutine 12 12 100.0
pod 4 4 100.0
total 130 149 87.2


line stmt bran cond sub pod time code
1             package Getopt::EX::Numbers;
2              
3             our $VERSION = "3.03";
4              
5 1     1   144814 use v5.14;
  1         5  
6 1     1   25 use warnings;
  1         2  
  1         72  
7              
8 1     1   7 use Carp;
  1         2  
  1         74  
9 1     1   7 use List::Util qw();
  1         2  
  1         27  
10 1     1   757 use Hash::Util qw(lock_keys);
  1         3577  
  1         5  
11 1     1   77 use Data::Dumper;
  1         1  
  1         43  
12             $Data::Dumper::Sortkeys = 1;
13              
14 1     1   3 use Exporter qw(import);
  1         1  
  1         761  
15             our @EXPORT_OK = qw();
16              
17             sub _default {
18             return {
19 6     6   30 min => 0,
20             max => undef,
21             start => '',
22             end => '',
23             step => '',
24             length => '',
25             _spec => undef,
26             };
27             }
28              
29             sub new {
30 6     6 1 159288 my $class = shift;
31 6         10 my $obj = bless _default(), $class;
32 6         10 lock_keys %{$obj};
  6         35  
33 6 50       67 @_ % 2 and croak "invalid number of parameters";
34 6         17 while (my($key, $value) = splice(@_, 0, 2)) {
35 15 50       22 croak "$key: invalid parameter" if not exists $obj->{$key};
36 15         32 $obj->{$key} = $value;
37             }
38 6         18 $obj;
39             }
40              
41             sub parse {
42 31     31 1 13997 my $obj = shift;
43 31         35 local $_ = shift;
44 31 50       192 if (m{
45             ^
46             (? -\d+ | \d* )
47             (?:
48             (?: \.\. | : ) (? [-+]\d+ | \d* )
49             (?:
50             : (? \d* )
51             (?:
52             : (? \d* )
53             )?
54             )?
55             )?
56             $
57             }x) {
58 31         140 $obj->{start} = $+{start};
59 31         84 $obj->{end} = $+{end};
60 31         79 $obj->{step} = $+{step};
61 31         68 $obj->{length} = $+{length};
62             }
63             else {
64 0         0 carp "$_: format error";
65 0         0 return undef;
66             }
67 31         41 $obj->{_spec} = $_;
68 31         77 $obj;
69             }
70              
71             sub range {
72 32     32 1 33 my $obj = shift;
73 32         40 my $max = $obj->{max};
74 32         31 my $min = $obj->{min};
75              
76 32         35 my $start = $obj->{start};
77 32         29 my $end = $obj->{end};
78 32         35 my $step = $obj->{step};
79 32         29 my $length = $obj->{length};
80              
81 32 100       53 if (not defined $max) {
82 2 50 33     12 if ($start =~ /^-\d+$/ or
      33        
83             (defined $end and $end =~ /^-\d+$/)) {
84 0         0 carp "$_: max required";
85 0         0 return ();
86             }
87             }
88              
89 32 50 100     132 if ($start =~ /\d/ and defined $max and $start > $max) {
      66        
90 0         0 return ();
91             }
92 32 100       72 if ($start eq '') {
    100          
93 17         18 $start = $min;
94             }
95             elsif ($start =~ /^-\d+$/) {
96 2         6 $start = List::Util::max($min, $start + $max);
97             }
98              
99 32 100       83 if (not defined $end) {
    100          
    100          
    100          
100 3         4 $end = $start;
101             }
102             elsif ($end eq '') {
103 7 50       11 $end = defined $max ? $max : $start;
104             }
105             elsif ($end =~ /^-/) {
106 2         5 $end = List::Util::max(0, $end + $max);
107             }
108             elsif ($end =~ s/^\+//) {
109 1         2 $end += $start;
110             }
111 32 100 100     103 $end = $max if defined $max and $end > $max;
112              
113 32   100     76 $length ||= 1;
114 32   66     58 $step ||= $length;
115              
116             # Ensure step is positive to avoid infinite loop
117 32 50       56 croak "step must be positive" if $step <= 0;
118              
119 32         32 my @l;
120 32 100       35 if ($step == 1) {
121 15         27 @l = ( [$start, $end] );
122             } else {
123 17         32 for (my $from = $start; $from <= $end; $from += $step) {
124 75         79 my $to = $from + $length - 1;
125 75 100       98 $to = List::Util::min($max, $to) if defined $max;
126 75         118 push @l, [$from, $to];
127             }
128             }
129              
130 32         69 return @l;
131             }
132              
133             sub sequence {
134 17     17 1 19 my $obj = shift;
135 17 50       30 map { ref $_ eq 'ARRAY' ? ($_->[0] .. $_->[1]) : $_ } $obj->range;
  57         123  
136             }
137              
138             1;
139              
140             __END__