#!/usr/bin/env perl
package My::Opts;
use FindBin qw( $RealBin );
use lib "$RealBin/lib";
use v5.32;
use Mojo::Base -strict;
use Getopt::Long qw( GetOptions );
use Mojo::Util qw( dumper );
use My::Array();
=head2 new
Input:
[
{
desc => "Option1",
spec => "opt1=s",
},
{
desc => "Option2",
spec => "opt2=s",
},
]
=cut
sub new {
my ( $class, $spec ) = @_;
my $s = bless {
_args => \@ARGV,
_spec => $spec,
}, $class;
$s->_init();
$s;
}
sub _init {
my ( $s ) = @_;
$s->_parse();
GetOptions( $s, $s->{_opts_spec}->@* ) or die "\n[$!]\n";
$s->debug() if $s->{debug};
$s->list_options() if $s->{list_options};
}
sub _parse {
my ( $s ) = @_;
my $parsed = $s->{_parsed} = $s->__parse();
$s->{_opts_spec} = [ map { $_->{spec} } @$parsed ];
$s->{_opts_list} = [ sort map { $_->{list}->@* } @$parsed ];
$s;
}
sub __parse {
my ( $s ) = @_;
my @parsed = map {
my $opt_spec = $_->{spec};
my $opt_desc = $_->{desc};
my @opt_list = split /\|/, $opt_spec;
my $arg = $1 if $opt_list[-1] =~ s/ (\W+.*) //x;
for ( @opt_list ) {
s/ (?=^\w{2,}) /--/x; # Long options.
s/ (?=^\w$) /-/x; # Short options.
}
{
key => $_,
spec => $opt_spec,
list => \@opt_list,
arg => $arg // "",
desc => $opt_desc // "...",
};
} $s->{_spec}->@*;
\@parsed;
}
sub debug {
my ( $s ) = @_;
delete $s->{_parsed} unless $s->{debug} > 1;
say dumper $s;
exit 1;
}
sub list_options {
my ( $s ) = @_;
say for $s->{_opts_list}->@*;
exit 1;
}
sub build_help_options {
my ( $s ) = @_;
my $indent = " " x 6;
my $parsed = $s->{_parsed};
my @line = map {
my $arg = _expand_arg( $_->{arg} );
my $opts_list = join ", ", $_->{list}->@*;
[ "$opts_list$arg", $_->{desc}, ]
} @$parsed;
my $max = My::Array->max_lengths( \@line );
my $format = $s->_make_format( $max );
my $output = join "\n$indent", map { sprintf $format, @$_; } @line;
$output;
}
sub _expand_arg {
local ( $_ ) = @_;
my $required = s/=//;
my $optional = s/:(\d+)/DEFAULT=$1/ or s/://;
my %means = (
s => "STRING",
i => "INTEGER",
'+' => "INCREMENT",
);
my $arg = $means{$_} // $_;
$arg = "[$arg]" if $optional;
$arg = " $arg" if $arg ne "";
$arg;
}
sub _make_format {
my ( $s, $max ) = @_;
join " # ", map { "%-${_}s" } @$max;
}
1;