File Coverage

blib/lib/Getopt/Long/Descriptive/Usage.pm
Criterion Covered Total %
statement 109 115 94.7
branch 38 52 73.0
condition 9 13 69.2
subroutine 15 16 93.7
pod 6 6 100.0
total 177 202 87.6


line stmt bran cond sub pod time code
1 2     2   14 use strict;
  2         5  
  2         58  
2 2     2   14 use warnings;
  2         4  
  2         102  
3             package Getopt::Long::Descriptive::Usage;
4             # ABSTRACT: the usage description for GLD
5             $Getopt::Long::Descriptive::Usage::VERSION = '0.109';
6 2     2   11 use List::Util qw(max);
  2         4  
  2         3115  
7              
8             #pod =head1 SYNOPSIS
9             #pod
10             #pod use Getopt::Long::Descriptive;
11             #pod my ($opt, $usage) = describe_options( ... );
12             #pod
13             #pod $usage->text; # complete usage message
14             #pod
15             #pod $usage->die; # die with usage message
16             #pod
17             #pod =head1 DESCRIPTION
18             #pod
19             #pod This document only describes the methods of the Usage object. For information
20             #pod on how to use L, consult its documentation.
21             #pod
22             #pod =head1 METHODS
23             #pod
24             #pod =head2 new
25             #pod
26             #pod my $usage = Getopt::Long::Descriptive::Usage->new(\%arg);
27             #pod
28             #pod You B don't need to call this. GLD will do it for you.
29             #pod
30             #pod Valid arguments are:
31             #pod
32             #pod options - an arrayref of options
33             #pod leader_text - the text that leads the usage; this may go away!
34             #pod
35             #pod =cut
36              
37             sub new {
38 29     29 1 81 my ($class, $arg) = @_;
39              
40 29         68 my @to_copy = qw(leader_text options show_defaults);
41              
42 29         76 my %copy;
43 29         110 @copy{ @to_copy } = @$arg{ @to_copy };
44              
45 29         103 bless \%copy => $class;
46             }
47              
48             #pod =head2 text
49             #pod
50             #pod This returns the full text of the usage message.
51             #pod
52             #pod =cut
53              
54             sub text {
55 15     15 1 125 my ($self) = @_;
56              
57 15         42 return join qq{\n}, $self->leader_text, $self->option_text;
58             }
59              
60             #pod =head2 leader_text
61             #pod
62             #pod This returns the text that comes at the beginning of the usage message.
63             #pod
64             #pod =cut
65              
66 15     15 1 151 sub leader_text { $_[0]->{leader_text} }
67              
68             #pod =head2 option_text
69             #pod
70             #pod This returns the text describing the available options.
71             #pod
72             #pod =cut
73              
74             sub option_text {
75 15     15 1 33 my ($self) = @_;
76              
77 15         28 my $string = q{};
78              
79 15 50       24 my @options = @{ $self->{options} || [] };
  15         59  
80 15         35 my @specs = map { $_->{spec} } grep { $_->{desc} ne 'spacer' } @options;
  39         81  
  50         115  
81 15   100     33 my $length = (max(map { _option_length($_) } @specs) || 0);
82 15         38 my $spec_fmt = "\t%-${length}s";
83              
84 15         37 while (@options) {
85 50         86 my $opt = shift @options;
86 50         93 my $spec = $opt->{spec};
87 50         79 my $desc = $opt->{desc};
88              
89 50 100       104 if ($desc eq 'spacer') {
90 11 100       24 if (ref $opt->{spec}) {
91 2         3 $string .= "${ $opt->{spec} }\n";
  2         6  
92 2         6 next;
93             } else {
94 9         18 my @lines = $self->_split_description($length, $opt->{spec});
95              
96 9 100       31 $string .= length($_) ? sprintf("$spec_fmt\n", $_) : "\n" for @lines;
97 9         26 next;
98             }
99             }
100              
101 39         94 ($spec, my $assign) = Getopt::Long::Descriptive->_strip_assignment($spec);
102              
103 39         92 my ($pre, $post) = _parse_assignment($assign);
104 39         109 my @names = split /\|/, $spec;
105              
106 39         73 my $primary = shift @names;
107 39         74 my $short;
108 39         82 my ($i) = grep {; length $names[$_] == 1 } (0 .. $#names);
  15         39  
109 39 100       81 if (defined $i) {
110 15         29 $short = splice @names, $i, 1;
111             }
112              
113 39 50       101 $spec = length $primary > 1 ? "--$pre$primary$post" : "-$primary$post";
114 39 100       80 $spec .= " (or -$short)" if $short;
115              
116 39         85 my @desc = $self->_split_description($length, $desc);
117              
118 39 50       76 if (@names) {
119             push @desc,
120 0 0       0 "aka " . join q{, }, map { length > 1 ? "--$_" : "-$_" } @names;
  0         0  
121             }
122              
123             # add default value if it exists
124 39 0 33     87 if (exists $opt->{constraint}->{default} and $self->{show_defaults}) {
125 0         0 my $dflt = $opt->{constraint}->{default};
126 0 0       0 $dflt = ! defined $dflt ? '(undef)'
    0          
127             : ! length $dflt ? '(empty string)'
128             : $dflt;
129 0         0 push @desc, "(default value: $dflt)";
130             }
131              
132 39         189 $string .= sprintf "$spec_fmt %s\n", $spec, shift @desc;
133 39         134 for my $line (@desc) {
134 2         4 $string .= "\t";
135 2         6 $string .= q{ } x ( $length + 2 );
136 2         6 $string .= "$line\n";
137             }
138             }
139              
140 15         105 return $string;
141             }
142              
143             sub _option_length {
144 39     39   71 my ($fullspec) = @_;
145              
146 39         98 my ($spec, $argspec) = Getopt::Long::Descriptive->_strip_assignment($fullspec);
147              
148 39         82 my ($pre, $post) = _parse_assignment($argspec);
149 39         108 my @names = split /\|/, $spec;
150              
151 39         71 my $primary = shift @names;
152 39 100 66     125 my $short = (@names && length $names[0] eq 1)
153             ? shift @names
154             : undef;
155              
156 39 50       100 $spec = length $primary > 1 ? "--$pre$primary$post" : "-$primary$post";
157 39 100       98 $spec .= " (or -$short)" if $short;
158              
159 39         122 return length $spec;
160             }
161              
162             sub _max_line_length {
163 48     48   99 return $Getopt::Long::Descriptive::TERM_WIDTH - 2;
164             }
165              
166             sub _split_description {
167 48     48   89 my ($self, $length, $desc) = @_;
168              
169             # 8 for a tab, 2 for the space between option & desc, 2 more for gutter
170 48         89 my $max_length = $self->_max_line_length - ( $length + 8 + 2 );
171              
172 48 100       146 return $desc if length $desc <= $max_length;
173              
174 3         4 my @lines;
175 3         8 while (length $desc > $max_length) {
176 5         12 my $idx = rindex( substr( $desc, 0, $max_length ), q{ }, );
177 5 100       10 last unless $idx >= 0;
178 4         10 push @lines, substr($desc, 0, $idx);
179 4         11 substr($desc, 0, $idx + 1) = q{};
180             }
181 3         8 push @lines, $desc;
182              
183 3         8 return @lines;
184             }
185              
186             sub _parse_assignment {
187 96     96   2296 my ($assign_spec) = @_;
188              
189 96         157 my $result = 'STR';
190 96         134 my $desttype;
191 96 100       231 if (length($assign_spec) < 2) {
192             # empty, ! or +
193 62 100       124 return ('[no-]', '') if $assign_spec eq '!';
194 58         131 return ('', '');
195             }
196              
197 34         80 my $optional = substr($assign_spec, 0, 1) eq ':';
198 34         60 my $argument = substr $assign_spec, 1, 2;
199              
200 34 100 100     195 if ($argument =~ m/^[io]/ or $assign_spec =~ m/^:[+0-9]/) {
    100          
201 10         20 $result = 'INT';
202             } elsif ($argument =~ m/^f/) {
203 4         8 $result = 'NUM';
204             }
205              
206 34 100       75 if (length($assign_spec) > 2) {
207 16         29 $desttype = substr($assign_spec, 2, 1);
208 16 100       37 if ($desttype eq '@') {
    50          
209             # Imply it can be repeated
210 8         16 $result .= '...';
211             } elsif ($desttype eq '%') {
212 8         20 $result = "KEY=${result}...";
213             }
214             }
215              
216 34 100       66 if ($optional) {
217 10         47 return ("", "[=$result]");
218             }
219              
220             # with leading space so it can just blindly be appended.
221 24         93 return ("", " $result");
222             }
223              
224             #pod =head2 warn
225             #pod
226             #pod This warns with the usage message.
227             #pod
228             #pod =cut
229              
230 0     0 1 0 sub warn { warn shift->text }
231              
232             #pod =head2 die
233             #pod
234             #pod This throws the usage message as an exception.
235             #pod
236             #pod $usage_obj->die(\%arg);
237             #pod
238             #pod Some arguments can be provided
239             #pod
240             #pod pre_text - text to be prepended to the usage message
241             #pod post_text - text to be appended to the usage message
242             #pod
243             #pod The C and C arguments are concatenated with the usage
244             #pod message with no line breaks, so supply this if you need them.
245             #pod
246             #pod =cut
247              
248             sub die {
249 3     3 1 6 my $self = shift;
250 3   50     10 my $arg = shift || {};
251              
252             die(
253 9         87 join q{}, grep { defined } $arg->{pre_text}, $self->text, $arg->{post_text}
254 3         20 );
255             }
256              
257             use overload (
258             q{""} => "text",
259              
260             # This is only needed because Usage used to be a blessed coderef that worked
261             # this way. Later we can toss a warning in here. -- rjbs, 2009-08-19
262             '&{}' => sub {
263 2     2   6 my ($self) = @_;
264 2         301 Carp::cluck("use of __PACKAGE__ objects as a code ref is deprecated");
265 2 50   2   110 return sub { return $_[0] ? $self->text : $self->warn; };
  2         9  
266             }
267 2     2   18 );
  2         5  
  2         17  
268              
269             1;
270              
271             __END__