File Coverage

blib/lib/MooX/Options/Descriptive/Usage.pm
Criterion Covered Total %
statement 79 157 50.3
branch 30 100 30.0
condition 3 14 21.4
subroutine 13 18 72.2
pod 7 7 100.0
total 132 296 44.5


line stmt bran cond sub pod time code
1             package MooX::Options::Descriptive::Usage;
2              
3 22     22   144 use strictures 2;
  22         167  
  22         886  
4              
5             =head1 NAME
6              
7             MooX::Options::Descriptive::Usage - Usage class
8              
9             =head1 DESCRIPTION
10              
11             Usage class to display the error message.
12              
13             This class use the full size of your terminal
14              
15             =cut
16              
17             ## no critic (ProhibitExcessComplexity)
18              
19             our $VERSION = "4.102";
20              
21 22     22   4821 use Getopt::Long::Descriptive;
  22         47  
  22         122  
22 22     22   6310 use Module::Runtime qw(use_module);
  22         50  
  22         173  
23 22     22   995 use Scalar::Util qw/blessed/;
  22         39  
  22         991  
24 22     22   6228 use Text::LineFold ();
  22         474559  
  22         546  
25              
26 22     22   1196 use Moo;
  22         6205  
  22         173  
27             with "MooX::Locale::Passthrough";
28              
29             has format_doc => ( is => "lazy" );
30              
31             ## no critic (Subroutines::RequireFinalReturn, Subroutines::ProhibitUnusedPrivateSubroutines)
32              
33             sub _build_format_doc {
34 3     3   31 my $self = shift;
35 3         11 +{ 's' => $self->__("String"),
36             's@' => $self->__("[Strings]"),
37             'i' => $self->__("Int"),
38             'i@' => $self->__("[Ints]"),
39             'o' => $self->__("Ext. Int"),
40             'o@' => $self->__("[Ext. Ints]"),
41             'f' => $self->__("Real"),
42             'f@' => $self->__("[Reals]"),
43             };
44             }
45              
46             has format_doc_long => ( is => "lazy" );
47              
48             sub _build_format_doc_long {
49 0     0   0 my $self = shift;
50 0         0 +{ 's' => $self->__("String"),
51             's@' => $self->__("Array of Strings"),
52             'i' => $self->__("Integer"),
53             'i@' => $self->__("Array of Integers"),
54             'o' => $self->__("Extended Integer"),
55             'o@' => $self->__("Array of extended integers"),
56             'f' => $self->__("Real number"),
57             'f@' => $self->__("Array of real numbers"),
58             };
59             }
60              
61             =head1 ATTRIBUTES
62              
63             Following attributes are present and behave as GLD::Usage describe them.
64              
65             =head2 leader_text
66              
67             Text that appear on top of your message
68              
69             =head2 options
70              
71             The options spec of your message
72              
73             =cut
74              
75             has leader_text => ( is => "ro" );
76             has options => ( is => "ro" );
77              
78             =head1 METHODS
79              
80             =head2 sub_commands_text
81              
82             Return the list of sub commands if available.
83              
84             =cut
85              
86             sub sub_commands_text {
87 45     45 1 105 my ($self) = @_;
88 45         92 my $sub_commands = [];
89 45 50 33     275 if (defined $self->{target}
90             && defined(
91             my $sub_commands_options = $self->{target}->_options_sub_commands
92             )
93             )
94             {
95 0         0 $sub_commands = $sub_commands_options;
96             }
97 45 50       829 return if !@$sub_commands;
98             return "",
99             $self->__("SUB COMMANDS AVAILABLE: ")
100 0         0 . join( ', ', map { $_->{name} } @$sub_commands ), "";
  0         0  
101             }
102              
103             =head2 text
104              
105             Return a compact help message.
106              
107             =cut
108              
109             sub text {
110 38     38 1 86 my ($self) = @_;
111             my %options_data
112 38 50       913 = defined $self->{target} ? $self->{target}->_options_data : ();
113             my %options_config
114             = defined $self->{target}
115             ? $self->{target}->_options_config
116 38 50       1132 : ( spacer => " " );
117 38         532 my $getopt_options = $self->options;
118              
119 38         93 my $lf = _get_line_fold();
120              
121 38         23079 my @to_fold;
122 38         65 my $max_spec_length = 0;
123 38         87 for my $opt (@$getopt_options) {
124 240 100       474 if ( $opt->{desc} eq 'spacer' ) {
125 40         60 push @to_fold, '';
126             push @to_fold,
127 40         119 $options_config{spacer} x ( $lf->config('ColMax') - 4 );
128 40         1396 next;
129             }
130 200         807 my ( $short, $format ) = $opt->{spec} =~ /(?:\|(\w))?(?:=(.*?))?$/x;
131 200         286 my $format_doc_str;
132 200 100       464 $format_doc_str = $self->format_doc->{$format} if defined $format;
133             $format_doc_str = 'JSON'
134 200 50       534 if defined $options_data{ $opt->{name} }{json};
135              
136             my $spec
137             = ( defined $short ? "-" . $short . " " : "" ) . "-"
138             . ( length( $opt->{name} ) > 1 ? "-" : "" )
139             . $opt->{name}
140 200 50       631 . ( defined $format_doc_str ? "=" . $format_doc_str : "" );
    100          
    100          
141              
142 200 100       348 $max_spec_length = length($spec) if $max_spec_length < length($spec);
143              
144 200         423 push @to_fold, $spec, $opt->{desc};
145             }
146              
147 38         60 my @message;
148 38         83 while (@to_fold) {
149 240         125399 my $spec = shift @to_fold;
150 240         346 my $desc = shift @to_fold;
151 240 100       448 if ( length($spec) ) {
152 200         1049 push @message,
153             $lf->fold(
154             " ",
155             " " x ( 6 + $max_spec_length ),
156             sprintf(
157             "%-" . ( $max_spec_length + 1 ) . "s %s",
158             $spec, $desc
159             )
160             );
161             }
162             else {
163 40         97 push @message, $desc, "\n";
164             }
165             }
166              
167 38         26532 return join( "\n",
168             $self->leader_text, "", join( "", @message ),
169             $self->sub_commands_text );
170             }
171              
172             # set the column size of your terminal into the wrapper
173             sub _get_line_fold {
174             my $columns = $ENV{TEST_FORCE_COLUMN_SIZE}
175 45   100 45   180 || eval {
176             use_module("Term::Size::Any");
177             [ Term::Size::Any::chars() ]->[0];
178             } || 80;
179              
180 45         1343 return Text::LineFold->new( ColMax => $columns - 4 );
181             }
182              
183             =head2 option_help
184              
185             Return the help message for your options
186              
187             =cut
188              
189             sub option_help {
190 7     7 1 21 my ($self) = @_;
191             my %options_data
192 7 50       167 = defined $self->{target} ? $self->{target}->_options_data : ();
193             my %options_config
194             = defined $self->{target}
195             ? $self->{target}->_options_config
196 7 50       195 : ( spacer => " " );
197 7         100 my $getopt_options = $self->options;
198 7         11 my @message;
199 7         17 my $lf = _get_line_fold();
200 7         3722 for my $opt (@$getopt_options) {
201 46 100       18078 if ( $opt->{desc} eq 'spacer' ) {
202             push @message,
203 9         31 $options_config{spacer} x ( $lf->config('ColMax') - 4 );
204 9         298 push @message, "";
205 9         19 next;
206             }
207 37         209 my ( $short, $format ) = $opt->{spec} =~ /(?:\|(\w))?(?:=(.*?))?$/x;
208 37         62 my $format_doc_str;
209 37 50       75 $format_doc_str = $self->format_doc->{$format} if defined $format;
210             $format_doc_str = 'JSON'
211 37 50       113 if defined $options_data{ $opt->{name} }{json};
212             push @message,
213             ( defined $short ? "-" . $short . " " : "" ) . "-"
214             . ( length( $opt->{name} ) > 1 ? "-" : "" )
215 37 50       187 . $opt->{name} . ":"
    100          
    50          
216             . ( defined $format_doc_str ? " " . $format_doc_str : "" );
217              
218 37         66 my $opt_data = $options_data{ $opt->{name} };
219 37 50       74 $opt_data = {} if !defined $opt_data;
220             push @message,
221             $lf->fold(
222             " ",
223             " ",
224             defined $opt_data->{long_doc}
225             ? $self->__( $opt_data->{long_doc} )
226             : $self->__( $opt->{desc} )
227 37 50       140 );
228             }
229              
230 7         3284 return join( "\n",
231             $self->leader_text, join( "\n ", "", @message ),
232             $self->sub_commands_text );
233             }
234              
235             =head2 option_pod
236              
237             Return the usage message in pod format
238              
239             =cut
240              
241             sub option_pod {
242 0     0 1 0 my ($self) = @_;
243              
244             my %options_data
245 0 0       0 = defined $self->{target} ? $self->{target}->_options_data : ();
246             my %options_config
247             = defined $self->{target}
248             ? $self->{target}->_options_config
249 0 0       0 : ( spacer => " " );
250              
251 0         0 my $prog_name = $self->{prog_name};
252 0 0       0 $prog_name = Getopt::Long::Descriptive::prog_name if !defined $prog_name;
253              
254 0         0 my $sub_commands = [];
255 0 0 0     0 if (defined $self->{target}
256             && defined(
257             my $sub_commands_options
258             = $self->{target}->_options_sub_commands()
259             )
260             )
261             {
262 0         0 $sub_commands = $sub_commands_options;
263             }
264              
265 0         0 my @man = ( "=encoding UTF-8", "=head1 NAME", $prog_name, );
266              
267 0 0       0 if ( defined( my $description = $options_config{description} ) ) {
268 0         0 push @man, "=head1 DESCRIPTION", $description;
269             }
270              
271 0         0 push @man,
272             (
273             "=head1 SYNOPSIS",
274             $prog_name . " [-h] [" . $self->__("long options ...") . "]"
275             );
276              
277 0 0       0 if ( defined( my $synopsis = $options_config{synopsis} ) ) {
278 0         0 push @man, $synopsis;
279             }
280              
281 0         0 push @man, ( "=head1 OPTIONS", "=over" );
282              
283 0         0 my $spacer_escape = "E<" . ord( $options_config{spacer} ) . ">";
284 0         0 for my $opt ( @{ $self->options } ) {
  0         0  
285 0 0       0 if ( $opt->{desc} eq 'spacer' ) {
286 0         0 push @man, "=back";
287 0         0 push @man, $spacer_escape x 40;
288 0         0 push @man, "=over";
289 0         0 next;
290             }
291 0         0 my ( $short, $format ) = $opt->{spec} =~ /(?:\|(\w))?(?:=(.*?))?$/x;
292 0         0 my $format_doc_str;
293 0 0       0 $format_doc_str = $self->format_doc_long->{$format}
294             if defined $format;
295             $format_doc_str = 'JSON'
296 0 0       0 if defined $options_data{ $opt->{name} }{json};
297              
298             my $opt_long_name
299 0 0       0 = "-" . ( length( $opt->{name} ) > 1 ? "-" : "" ) . $opt->{name};
300 0 0       0 my $opt_name
    0          
301             = ( defined $short ? "-" . $short . " " : "" )
302             . $opt_long_name . ":"
303             . ( defined $format_doc_str ? " " . $format_doc_str : "" );
304              
305 0         0 push @man, "=item B<" . $opt_name . ">";
306              
307 0         0 my $opt_data = $options_data{ $opt->{name} };
308 0 0       0 $opt_data = {} if !defined $opt_data;
309             push @man, defined $opt_data->{long_doc}
310             ? $opt_data->{long_doc}
311 0 0       0 : $opt->{desc};
312             }
313 0         0 push @man, "=back";
314              
315 0 0       0 if (@$sub_commands) {
316 0         0 push @man, "=head1 AVAILABLE SUB COMMANDS";
317 0         0 push @man, "=over";
318 0         0 for my $sub_command (@$sub_commands) {
319 0 0 0     0 if ($sub_command->{command}->can("_options_config")
320             && defined(
321             my $desc
322             = { $sub_command->{command}->_options_config }
323             ->{description}
324             )
325             )
326             {
327 0         0 push @man, "=item B<" . $sub_command->{name} . "> : " . $desc;
328             }
329             else {
330 0         0 push @man, "=item B<" . $sub_command->{name} . "> :";
331             }
332              
333             push @man,
334             $prog_name . " "
335             . $sub_command->{name}
336 0         0 . " [-h] ["
337             . $self->__("long options ...") . "]";
338             }
339 0         0 push @man, "=back";
340             }
341              
342 0 0       0 if ( defined( my $authors = $options_config{authors} ) ) {
343 0 0 0     0 if ( !ref $authors && length($authors) ) {
344 0         0 $authors = [$authors];
345             }
346 0 0       0 if (@$authors) {
347 0         0 push @man, ( "=head1 AUTHORS", "=over" );
348 0         0 push @man, map { "=item B<" . $_ . ">" } @$authors;
  0         0  
349 0         0 push @man, "=back";
350             }
351             }
352              
353 0         0 return join( "\n\n", @man );
354             }
355              
356             =head2 option_short_usage
357              
358             All options message without help
359              
360             =cut
361              
362             sub option_short_usage {
363 0     0 1 0 my ($self) = @_;
364             my %options_data
365 0 0       0 = defined $self->{target} ? $self->{target}->_options_data : ();
366 0         0 my $getopt_options = $self->options;
367              
368 0         0 my $prog_name = $self->{prog_name};
369 0 0       0 $prog_name = Getopt::Long::Descriptive::prog_name if !defined $prog_name;
370              
371 0         0 my @message;
372 0         0 for my $opt (@$getopt_options) {
373 0 0       0 if ( $opt->{desc} eq 'spacer' ) {
374 0         0 push @message, '';
375 0         0 next;
376             }
377 0         0 my ($format) = $opt->{spec} =~ /(?:\|\w)?(?:=(.*?))?$/x;
378 0         0 my $format_doc_str;
379 0 0       0 $format_doc_str = $self->format_doc->{$format} if defined $format;
380             $format_doc_str = 'JSON'
381 0 0       0 if defined $options_data{ $opt->{name} }{json};
382             push @message,
383             "-"
384             . ( length( $opt->{name} ) > 1 ? "-" : "" )
385             . $opt->{name}
386 0 0       0 . ( defined $format_doc_str ? "=" . $format_doc_str : "" );
    0          
387             }
388             return
389 0 0       0 join( " ", $prog_name, map { $_ eq '' ? " | " : "[ $_ ]" } @message );
  0         0  
390             }
391              
392             =head2 warn
393              
394             Warn your options help message
395              
396             =cut
397              
398 0     0 1 0 sub warn { return CORE::warn shift->text }
399              
400             =head2 die
401              
402             Croak your options help message
403              
404             =cut
405              
406             sub die {
407 5     5 1 6451 my ($self) = @_;
408 5         18 $self->{should_die} = 1;
409 5         15 return;
410             }
411              
412             use overload (
413             q{""} => "text",
414             '&{}' => sub {
415             return
416 0 0   0   0 sub { my ($self) = @_; return $self ? $self->text : $self->warn; };
  0         0  
  0         0  
417             }
418 22     22   46580 );
  22         51  
  22         211  
419              
420             =head1 SUPPORT
421              
422             You can find documentation for this module with the perldoc command.
423              
424             perldoc MooX::Options
425              
426             You can also look for information at:
427              
428             =over 4
429              
430             =item * RT: CPAN's request tracker (report bugs here)
431              
432             L
433              
434             =item * AnnoCPAN: Annotated CPAN documentation
435              
436             L
437              
438             =item * CPAN Ratings
439              
440             L
441              
442             =item * Search CPAN
443              
444             L
445              
446             =back
447              
448             =head1 AUTHOR
449              
450             celogeek
451              
452             =head1 COPYRIGHT AND LICENSE
453              
454             This software is copyright (c) 2013 by celogeek .
455              
456             This software is copyright (c) 2017 by Jens Rehsack.
457              
458             This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
459              
460             =cut
461              
462             1;