1package App::Yath::Options;
2use strict;
3use warnings;
4
5our $VERSION = '1.000082';
6
7use Carp qw/croak confess/;
8use Scalar::Util qw/blessed/;
9use Test2::Harness::Util qw/mod2file/;
10
11use App::Yath::Option();
12use Test2::Harness::Settings();
13
14use Test2::Harness::Util::HashBase qw{
15    <all <lookup
16
17    <pre_list <cmd_list <post_list
18
19    <post_list_sorted
20
21    <settings
22
23    <args
24    <command_class
25
26    <pending_pre <pending_cmd <pending_post
27
28    <used_plugins
29
30    <included
31
32    <set_by_cli
33};
34
35sub import {
36    my $class  = shift;
37    my $caller = caller();
38
39    croak "$caller already has an 'options' method"
40        if defined(&{"$caller\::options"});
41
42    my @common;
43    my $instance;
44    my $options = sub { ($instance //= $class->new()) };
45    my $option  = sub { ($instance //= $class->new())->_option([caller()], shift(@_), @common ? (%{$common[-1]}) : (), @_) };
46    my $include = sub { ($instance //= $class->new())->include_from(@_) };
47
48    my $post = sub {
49        my $cb = pop;
50        my $weight = shift // 0;
51        my ($applicable) = @_;
52
53        $applicable //= $common[-1]->{applicable} if @common;
54
55        croak "You must provide a callback coderef" unless $cb && ref($cb) eq 'CODE';
56
57        ($instance //= $class->new())->_post($weight, $applicable, $cb);
58    };
59
60    my $group = sub {
61        my ($set, $sub) = @_;
62
63        my $common = {@common ? (%{$common[-1]}) : (), %$set};
64
65        if (my $class = $common->{builds}) {
66            require(mod2file($class));
67        }
68
69        push @common => $common;
70        my $ok  = eval { $sub->(); 1 };
71        my $err = $@;
72        pop @common;
73
74        die $err unless $ok;
75    };
76
77    {
78        no strict 'refs';
79        *{"$caller\::post"}            = $post;
80        *{"$caller\::option"}          = $option;
81        *{"$caller\::options"}         = $options;
82        *{"$caller\::option_group"}    = $group;
83        *{"$caller\::include_options"} = $include;
84    }
85
86    return 1;
87}
88
89sub init {
90    my $self = shift;
91
92    $self->{+ALL}   //= [];
93    $self->{+LOOKUP} //= {};
94
95    $self->{+USED_PLUGINS} //= [];
96
97    $self->{+PRE_LIST} //= [];
98    $self->{+CMD_LIST} //= [];
99    $self->{+POST_LIST} //= [];
100
101    $self->{+SETTINGS} //= Test2::Harness::Settings->new();
102
103    $self->{+INCLUDED} //= {};
104
105    $self->{+SET_BY_CLI} //= {};
106
107    return $self;
108}
109
110sub option {
111    my $self = shift;
112    $self->_option([caller()], @_);
113}
114
115sub include {
116    my $self = shift;
117    my ($inc) = @_;
118
119    croak "Include must be an instance of ${ \__PACKAGE__ }, got ${ defined($inc) ? \qq['$inc'] : \'undef' }"
120        unless $inc && blessed($inc) && $inc->isa(__PACKAGE__);
121
122    $self->include_option($_) for @{$inc->all};
123
124    $self->{+POST_LIST_SORTED} = 0;
125    push @{$self->{+POST_LIST}} => @{$inc->post_list};
126
127    return;
128}
129
130sub include_from {
131    my $self = shift;
132
133    for my $pkg (@_) {
134        require(mod2file($pkg)) unless $pkg->can('options');
135
136        next unless $pkg->can('options');
137        my $options = $pkg->options or next;
138        $self->include($options);
139
140        $self->{+INCLUDED}->{$pkg}++;
141        $self->{+INCLUDED}->{$_}++ for keys %{$options->included};
142    }
143
144    return;
145}
146
147sub populate_pre_defaults {
148    my $self = shift;
149
150    for my $opt (@{$self->_pre_command_options}) {
151        my $slot = $opt->option_slot($self->{+SETTINGS});
152        my $val = $opt->get_default($self->{+SETTINGS});
153        $$slot //= $val;
154    }
155}
156
157sub populate_cmd_defaults {
158    my $self = shift;
159
160    croak "The 'command_class' attribute has not yet been set"
161        unless $self->{+COMMAND_CLASS};
162
163    for my $opt (@{$self->_command_options()}) {
164        my $slot = $opt->option_slot($self->{+SETTINGS});
165        my $val = $opt->get_default($self->{+SETTINGS});
166        $$slot //= $val;
167    }
168}
169
170sub grab_pre_command_opts {
171    my $self = shift;
172    my %config = @_;
173
174    $self->populate_pre_defaults();
175
176    unshift @{$self->{+PENDING_PRE} //= []} => $self->_grab_opts(
177        '_pre_command_options',
178        'pre-command',
179        stop_at_non_opt => 1,
180        passthrough => 1,
181        %config,
182    );
183}
184
185sub process_pre_command_opts {
186    my $self = shift;
187    return unless $self->{+PENDING_PRE};
188    $self->_process_opts(delete $self->{+PENDING_PRE});
189}
190
191sub set_command_class {
192    my $self = shift;
193    my ($in) = @_;
194
195    croak "Command class has already been set"
196        if $self->{+COMMAND_CLASS};
197
198    my $class = blessed($in) || $in;
199
200    croak "Invalid command class: $class"
201        unless $class->isa('App::Yath::Command');
202
203    $self->include_from($class) if $class->can('options');
204
205    return $self->{+COMMAND_CLASS} = $class;
206}
207
208sub set_args {
209    my $self = shift;
210    my ($in) = @_;
211
212    croak "'args' has already been set"
213        if $self->{+ARGS};
214
215    return $self->{+ARGS} = $in;
216}
217
218sub grab_command_opts {
219    my $self = shift;
220    my %config = @_;
221
222    croak "The 'command_class' attribute has not yet been set"
223        unless $self->{+COMMAND_CLASS};
224
225    $self->populate_cmd_defaults();
226
227    push @{$self->{+PENDING_CMD} //= []} => $self->_grab_opts(
228        '_command_options',
229        "command (" . $self->{+COMMAND_CLASS}->name . ")",
230        %config,
231    );
232}
233
234sub process_command_opts {
235    my $self = shift;
236    return unless $self->{+PENDING_CMD};
237    $self->_process_opts(delete $self->{+PENDING_CMD});
238}
239
240sub process_option_post_actions {
241    my $self = shift;
242    my ($cmd) = @_;
243
244    croak "The 'args' attribute has not yet been set"
245        unless $self->{+ARGS};
246
247    if ($cmd) {
248        croak "The 'command_class' attribute has not yet been set"
249            unless $self->{+COMMAND_CLASS};
250
251        croak "The process_option_post_actions requires an App::Yath::Command instance, got: " . ($cmd // "undef")
252            unless blessed($cmd) && $cmd->isa('App::Yath::Command');
253
254        croak "The command '$cmd' dos not match the expected class '$self->{+COMMAND_CLASS}'"
255            unless blessed($cmd) eq $self->{+COMMAND_CLASS};
256    }
257
258    unless ($self->{+POST_LIST_SORTED}++) {
259        @{$self->{+POST_LIST}} = sort { $a->[0] <=> $b->[0] } @{$self->{+POST_LIST}};
260    }
261
262    for my $post (@{$self->{+POST_LIST}}) {
263        next if $post->[1] && !$post->[1]->($post->[2], $self);
264        $post->[2]->(
265            options  => $self,
266            args     => $self->{+ARGS},
267            settings => $self->{+SETTINGS},
268            $cmd ? (command => $cmd) : (),
269        );
270    }
271}
272
273sub _pre_command_options { $_[0]->{+PRE_LIST} }
274
275sub _command_options {
276    my $self = shift;
277
278    my $class = $self->{+COMMAND_CLASS} or croak "The 'command_class' attribute has not yet been set";
279
280    my $cmd = $class->name;
281    my $cmd_options = $self->{+CMD_LIST} // [];
282    my $pre_options = $self->{+PRE_LIST} // [];
283
284    return [grep { $_->applicable($self) } @$cmd_options, @$pre_options];
285}
286
287sub _process_opts {
288    my $self = shift;
289    my ($list) = @_;
290
291    while (my $opt_set  = shift @$list) {
292        my ($opt, $meth, @args) = @$opt_set;
293        $opt->$meth(@args, $self->{+SETTINGS}, $self, $list);
294        $self->{+SET_BY_CLI}->{$opt->prefix}->{$opt->field}++;
295        push @{$self->{+USED_PLUGINS}} => $opt->from_plugin if $opt->from_plugin;
296    }
297}
298
299sub _parse_long_option {
300    my $self = shift;
301    my ($arg) = @_;
302
303    $arg =~ m/^--((?:no-)?([^=]+))(=(.*))?$/ or confess "Invalid long option: $arg";
304
305    #return (main, full, val);
306    return ($2, $1, $3 ? $4 // '' : undef);
307}
308
309sub _parse_short_option {
310    my $self = shift;
311    my ($arg) = @_;
312
313    $arg =~ m/^-([^-])(=)?(.+)?$/ or confess "Invalid short option: $arg";
314
315    #return (main, remain, assign);
316    return ($1, $3, $2);
317}
318
319sub _handle_long_option {
320    my $self = shift;
321    my ($arg, $lookup, $args) = @_;
322
323    my ($main, $full, $val) = $self->_parse_long_option($arg);
324
325    my $opt;
326    if ($opt = $lookup->{long}->{$full}) {
327        if ($opt->requires_arg) {
328            $val //= shift(@$args) // die "Option --$full requires an argument.\n";
329        }
330        elsif($opt->allows_arg) {
331            $val //= 1;
332        }
333        else {
334            die "Option --$full does not take an argument\n" if defined $val;
335            $val = 1;
336        }
337
338        return [$opt, 'handle', $val];
339    }
340    elsif ($opt = $lookup->{long}->{$main}) {
341        die "Option --$full does not take an argument\n" if defined $val;
342        return [$opt, 'handle_negation'];
343    }
344
345    return undef;
346}
347
348sub _handle_short_option {
349    my $self = shift;
350    my ($arg, $lookup, $args) = @_;
351
352    my ($main, $remain, $assign) = $self->_parse_short_option($arg);
353
354    if (my $opt = $lookup->{short}->{$main}) {
355        my $val = 1;
356        if ($opt->allows_arg) {
357            $val = $remain;
358
359            $val //= '' if $assign;
360
361            if ($opt->requires_arg) {
362                $val //= shift(@$args) // die "Option -$main requires an argument.\n";
363            }
364            else {
365                $val //= 1;
366            }
367
368            return [$opt, 'handle', $val];
369        }
370        elsif ($assign) {
371            die "Option -$main does not take an argument\n";
372        }
373        elsif(defined($remain) && length($remain)) {
374            unshift @$args => "-$remain";
375        }
376
377        return [$opt, 'handle', $val];
378    }
379
380    return undef;
381}
382
383my %ARG_ENDS = ('--' => 1, '::' => 1);
384
385sub _grab_opts {
386    my $self = shift;
387    my ($opt_fetch, $type, %config) = @_;
388
389    croak "The opt_fetch callback is required" unless $opt_fetch;
390    croak "The arg type is required"   unless $type;
391
392    my $args = $config{args} || $self->{+ARGS} or confess "The 'args' attribute has not yet been set";
393
394    my $lookup = $self->_build_lookup($self->$opt_fetch());
395
396    my (@keep_args, @opts);
397    while (@$args) {
398        my $arg = shift @$args;
399
400        if ($ARG_ENDS{$arg}) {
401            push @keep_args => $arg;
402            last;
403        }
404
405        if (substr($arg, 0, 1) eq '-') {
406            my $handler = (substr($arg, 1, 1) eq '-') ? '_handle_long_option' : '_handle_short_option';
407            if(my $opt_set = $self->$handler($arg, $lookup, $args)) {
408                my ($opt, $action, @val) = @$opt_set;
409
410                if (my $pre = $opt->pre_process) {
411                    $pre->(
412                        opt          => $opt,
413                        options      => $self,
414                        action       => $action,
415                        type         => $type,
416
417                        @val ? (val => $val[0]) : (),
418                    );
419                }
420
421                $lookup = $self->_build_lookup($self->$opt_fetch())
422                    if $opt->adds_options;
423
424                push @opts => $opt_set;
425                next;
426            }
427            elsif (!$config{passthrough}) {
428                die "Invalid $type option: $arg\n";
429            }
430        }
431
432        die "Invalid $type option: $arg" if $config{die_at_non_opt};
433
434        push @keep_args => $arg;
435
436        last if $config{stop_at_non_opt};
437    }
438
439    unshift @$args => @keep_args;
440
441    return @opts;
442}
443
444sub _build_lookup {
445    my $self = shift;
446    my ($opts) = @_;
447
448    my $lookup = {long => {}, short => {}};
449
450    my %seen;
451    for my $opt (@$opts) {
452        next if $seen{$opt}++;
453
454        for my $long ($opt->long_args) {
455            $lookup->{long}->{$long} //= $opt;
456        }
457
458        my $short = $opt->short or next;
459        $lookup->{short}->{$short} //= $opt;
460    }
461
462    return $lookup;
463}
464
465sub _post {
466    my $self = shift;
467    my ($weight, $applicable, $cb) = @_;
468
469    $self->{+POST_LIST_SORTED} = 0;
470
471    $weight //= 0;
472
473    push @{$self->{+POST_LIST} //= []} => [$weight, $applicable, $cb];
474}
475
476sub _option {
477    my $self = shift;
478    my ($trace, @spec) = @_;
479
480    my %proto = $self->_parse_option_args(@spec);
481
482    my $opt = App::Yath::Option->new(
483        trace => $trace,
484        $self->_parse_option_caller($trace->[0], \%proto),
485        %proto,
486    );
487
488    $self->include_option($opt);
489}
490
491sub include_option {
492    my $self = shift;
493    my ($opt) = @_;
494
495    my $trace = $opt->trace or confess "Options must have a trace!";
496
497    push @{$self->{+ALL}} => $opt;
498
499    my $new = $self->_index_option($opt);
500    $self->_list_option($opt) if $new;
501
502    return $opt;
503}
504
505sub _parse_option_caller {
506    my $self = shift;
507    my ($caller, $proto) = @_;
508
509    my ($from_plugin, $from_command, $from_prefix, $prefix, $is_top);
510
511    $prefix = $proto->{prefix} if exists $proto->{prefix};
512    $prefix //= $caller->option_prefix() if $caller->can('option_prefix');
513
514    if ($caller->isa('App::Yath::Command')) {
515        $from_command = $caller->name() unless $caller eq 'App::Yath::Command';
516        $is_top       = 1;
517    }
518    elsif ($caller =~ m/App::Yath::Command::([^:]+)::.*Options(?:::.*)?$/) {
519        $from_command = $1;
520        $is_top       = 1;
521    }
522    elsif ($caller eq 'App::Yath') {
523        $is_top = 1;
524    }
525    elsif ($caller =~ m/^(App::Yath::Plugin::([^:]+))$/) {
526        $from_plugin = $1;
527        $from_prefix = $2;
528
529        unless (defined $prefix) {
530            $prefix = $from_prefix;
531            $prefix =~ s/::.*$//g;
532        }
533    }
534
535    $prefix = lc($prefix) if $prefix;
536
537    croak "Could not find an option prefix and option is not top-level ($proto->{title})"
538        unless $is_top || defined($prefix) || defined($proto->{prefix});
539
540    return (
541        $from_plugin          ? (from_plugin  => $from_plugin)  : (),
542        $from_command         ? (from_command => $from_command) : (),
543        ($prefix || !$is_top) ? (prefix       => $prefix)       : (),
544    );
545}
546
547sub _parse_option_args {
548    my $self = shift;
549    my @spec = @_;
550
551    my %args;
552    if (@spec == 1) {
553        my ($title, $type) = $spec[0] =~ m/^([\w-]+)(?:=(.+))?$/ or croak "Invalid option specification: $spec[0]";
554        return (title => $title, type => $type);
555    }
556    elsif (@spec == 2) {
557        my ($title, $type) = @spec;
558        return (title => $title, type => $type);
559    }
560
561    my $title = shift @spec;
562    return (title => $title, @spec);
563}
564
565sub _index_option {
566    my $self = shift;
567    my ($opt) = @_;
568
569    my $index = $self->{+LOOKUP};
570
571    my $out = 0;
572
573    for my $n ($opt->name, @{$opt->alt || []}) {
574        if (my $existing = $index->{$n}) {
575            next if "$existing" eq "$opt";
576            croak "Option '$n' was already defined (" . $existing->trace_string . ")";
577        }
578
579        $out++;
580        $index->{$n} = $opt;
581    }
582
583    if (my $short = $opt->short) {
584        if (my $existing = $index->{$short}) {
585            return $out if "$existing" eq "$opt";
586            croak "Option '$short' was already defined (" . $existing->trace_string . ")";
587        }
588
589        $out++;
590        $index->{$short} = $opt;
591    }
592
593    return $out;
594}
595
596sub _list_option {
597    my $self = shift;
598    my ($opt) = @_;
599
600    return push @{$self->{+PRE_LIST}} => $opt
601        if $opt->pre_command;
602
603    push @{$self->{+CMD_LIST}} => $opt;
604}
605
606sub pre_docs {
607    my $self = shift;
608
609    return $self->_docs($self->_pre_command_options(), @_);
610}
611
612sub cmd_docs {
613    my $self = shift;
614
615    return unless $self->{+COMMAND_CLASS};
616
617    return $self->_docs([grep { !$_->pre_command } @{$self->_command_options()}], @_);
618}
619
620my %DOC_FORMATS = (
621    'cli' => [
622        'cli_docs',    # Method to call on opt
623        "\n",          # how to join lines
624        sub { "\n$_[1]" },                        # how to render the category
625        sub { $_[0] =~ s/^/  /mg; "$_[0]\n" },    # transform the value from the opt
626        sub { },                                  # add this at the end
627    ],
628    'pod' => [
629        'pod_docs',                               # Method to call on opt
630        "\n\n",                                   # how to join lines
631        sub { ($_[0] ? ("=back") : (), "=head$_[2] $_[1]", "=over 4") },    # how to render the category
632        sub { $_[0] },                                                  # transform the value from the opt
633        sub { $_[0] ? ("=back") : () },                                 # add this at the end
634    ],
635);
636
637sub _docs {
638    my $self = shift;
639    my ($opts, $format, @args) = @_;
640
641    $format //= "UNDEFINED";
642    my $fset = $DOC_FORMATS{$format} or croak "Invalid documentation format '$format'";
643    my ($fmeth, $join, $fcat, $ftrans, $fend) = @$fset;
644
645    return unless $opts;
646    return unless @$opts;
647
648    my @opts = sort _doc_sort_ops @$opts;
649
650    my @out;
651
652    my $cat;
653    for my $opt (@opts) {
654        if (!$cat || $opt->category ne $cat) {
655            push @out => $fcat->($cat, $opt->category, @args);
656            $cat = $opt->category;
657        }
658
659        my $help = $opt->$fmeth();
660        push @out => $ftrans->($help);
661    }
662
663    push @out => $fend->($cat);
664
665    return join $join => @out;
666}
667
668sub _doc_sort_ops($$) {
669    my ($a, $b) = @_;
670
671    my $anc = $a->category eq 'NO CATEGORY - FIX ME';
672    my $bnc = $b->category eq 'NO CATEGORY - FIX ME';
673
674    if($anc xor $bnc) {
675        return 1 if $anc;
676        return -1;
677    }
678
679    my $ret = $a->category cmp $b->category;
680    $ret ||= ($a->prefix || '') cmp ($b->prefix || '');
681    $ret ||= $a->field cmp $b->field;
682    $ret ||= $a->name cmp $b->name;
683
684    return $ret;
685}
686
687sub clear_env {
688    my $self = shift;
689
690    for my $opt (@{$self->{+ALL}}) {
691        next unless $opt->clear_env_vars;
692        my $env = $opt->env_vars or next;
693        for my $var (@$env) {
694            $var =~ s/^!//;
695            delete $ENV{$var};
696        }
697    }
698}
699
7001;
701
702__END__
703
704=pod
705
706=encoding UTF-8
707
708=head1 NAME
709
710App::Yath::Options - Tools for defining and tracking yath CLI options.
711
712=head1 DESCRIPTION
713
714This class represents a collection of options, and holds the logic for
715processing them. This package also exports sugar to help you define options.
716
717=head1 SYNOPSIS
718
719    package My::Options;
720
721    use App::Yath::Options;
722
723    # This package now has a package instance of options, which can be obtained
724    # via the options() method.
725    my $options = __PACKAGE__->options;
726
727    # We can include options from other packages
728    include_options(
729        'Package::With::Options::A',
730        'Package::With::Options::B',
731        ...,
732    );
733
734    # Define an option group with some options
735    option_group { %common_fields } => sub {
736
737        # Define an option
738        option foo => (
739            type => 's',
740            default => "FOOOOOOO",
741            category => 'foo',
742            description => "This is foo"
743            long_examples => [' value'],
744            ...
745        );
746
747        option bar => ( ... );
748        ...
749    };
750
751    # Action to call right after options are parsed.
752    post sub {
753        my %params = @_;
754
755        ...
756    };
757
758=head1 EXPORTS
759
760=over 4
761
762=item $opts = options()
763
764=item $opts = $class->options()
765
766This returns the options instance associated with your package.
767
768=item include_options(@CLASSES)
769
770This lets you include options defined in other packages.
771
772=item option_group \%COMMON_FIELDS => sub { ... }
773
774An option group is simply a block where all calls to C<option()> will have
775common fields added automatically, this makes it easier to define multiple
776options that share common fields. Common fields can be overridden inside the
777option definition.
778
779These are both equivelent:
780
781    # Using option group
782    option_group { category => 'foo', prefix => 'foo' } => sub {
783        option a => (type => 'b');
784        option b => (type => 's');
785    };
786
787    # Not using option group
788    option a => (type => 'b', category => 'foo', prefix => 'foo');
789    option b => (type => 's', category => 'foo', prefix => 'foo');
790
791=item option TITLE => %FIELDS
792
793Define an option. The first argument is the C<title> attribute for the new
794option, all other arguments should be attribute/value pairs used to construct
795the option. See L<App::Yath::Option> for the documentation of attributes.
796
797=item post sub { ... }
798
799=item post $weight => sub { ... }
800
801C<post> callbacks are run after all command line arguments have been processed.
802This is a place to verify the result of several options combined, sanity check,
803or even add short-circuit behavior. This is how the C<--help> and
804C<--show-opts> options are implemented.
805
806If no C<$weight> is specified then C<0> is used. C<post> callbacks or sorted
807based on weight with higher values being run later.
808
809=back
810
811=head1 OPTIONS INSTANCES
812
813In general you should not be using the options instance directly. Options
814instances are mostly an implementation detail that should be treated as a black
815box. There are however a few valid reasons to interact with them directly. In
816those cases there are a few public attributes/methods you can work with. This
817section documents the public interface.
818
819=head2 ATTRIBUTES
820
821This section only lists attributes that may be useful to people working with
822options instances. There are a lot of internal (to yath) attributes that are
823implementation details that are not listed here. Attributes not listed here are
824not intended for external use and may change at any time.
825
826=over 4
827
828=item $arrayref = $options->all
829
830Arrayref containing all the L<App::Yath::Option> instances in the options
831instance.
832
833=item $settings = $options->settings
834
835Get the L<Test2::Harness::Settings> instance.
836
837=item $arrayref = $options->args
838
839Get the reference to the list of command line arguments. This list is modified
840as arguments are processed, there are no guarentees about what is in here at
841any given stage of argument processing.
842
843=item $class_name = $options->command_class
844
845If yath has determined what command is being executed this will be populated
846with that command class. This will be undefined if the class has not been
847determined yet.
848
849=item $arrayref = $options->used_plugins
850
851This is a list of all plugins who's options have been used. Plugins may appear
852more than once.
853
854=item $hashref = $options->included
855
856A hashref where every key is a package who's options have been included into
857this options instance. The values are an implementation detail, do not rely on
858them.
859
860=back
861
862=head2 METHODS
863
864This section only lists methods that may be useful to people working with
865options instances. There are a lot of internal (to yath) methods that are
866implementation details that are not listed here. Methods not listed here are
867not intended for external use and may change at any time.
868
869=over 4
870
871=item $opt = $options->option(%OPTION_ATTRIBUTES)
872
873This will create a new option with the provided attributes and add it to the
874options instance. A C<trace> attribute will be automatically set for you.
875
876=item $options->include($options_instance)
877
878This method lets you directly include options from a second instance into the
879first.
880
881=item $options->include_from(@CLASSES)
882
883This lets you include options from multiple classes that have options defined.
884
885=item $options->include_option($opt)
886
887This lets you include a single already defined option instance.
888
889=item $options->pre_docs($format, @args)
890
891Get documentation for pre-command options. $format may be 'cli' or 'pod'.
892
893=item $options->cmd_docs($format, @args)
894
895Get documentation for command options. $format may be 'cli' or 'pod'.
896
897=back
898
899=head1 SOURCE
900
901The source code repository for Test2-Harness can be found at
902F<http://github.com/Test-More/Test2-Harness/>.
903
904=head1 MAINTAINERS
905
906=over 4
907
908=item Chad Granum E<lt>exodist@cpan.orgE<gt>
909
910=back
911
912=head1 AUTHORS
913
914=over 4
915
916=item Chad Granum E<lt>exodist@cpan.orgE<gt>
917
918=back
919
920=head1 COPYRIGHT
921
922Copyright 2020 Chad Granum E<lt>exodist7@gmail.comE<gt>.
923
924This program is free software; you can redistribute it and/or
925modify it under the same terms as Perl itself.
926
927See F<http://dev.perl.org/licenses/>
928
929=cut
930