package Getopt::Dakkar::Role::Command;
use Getopt::Dakkar::Style qw(role);
use Getopt::Dakkar::ArgPack;
use Module::Runtime qw(use_module);
with 'Getopt::Dakkar::Role::Piece';
has options => ( is => 'ro', isa => ArrayRef, default => sub { [] } );
has parameters => ( is => 'ro', isa => ArrayRef, default => sub { [] } );
has subcommands => ( is => 'ro', isa => ArrayRef, default => sub { [] } );
has class => ( is => 'ro', isa => Str );
has object => ( is => 'ro', isa => Object );
has op => ( is => 'ro', isa => Str|CodeRef, default => 'merge_with_argpack' );
sub make_stash($self,$argpack) {
if (my $o = $self->object) { return $o }
return unless $self->class;
my $class = use_module($self->class);
if (my $from_argpack = $class->can('new_from_argpack')) {
return $class->$from_argpack($argpack);
}
else {
return $class->new();
}
}
sub parse($self,$args,$stash) {
$stash = $self->_parse($args,$stash);
if (my $op = $self->op) {
return $stash->$op();
}
return $stash;
}
sub _parse($self,$args,$stash) {
my $argpack = Getopt::Dakkar::ArgPack->new({getopt=>$self});
my @parameters = $self->parameters->@*;
while ($args->@*) {
my $this = $args->[0];
if ($self->_looks_like_option($this)
and not $self->_is_terminator($this)) {
if (my $option = $self->_match_option($this)) {
my $option_value = $option->parse($args,$stash);
$argpack->options->{$option_value->name} = $option_value;
}
else {
Getopt::Dakkar::X::BadOption->throw({
input => $this,
});
}
}
elsif (my $subcommand = $self->_match_subcommand($this)) {
shift $args->@*;
$subcommand->parse($args,$stash);
}
elsif ($self->_is_terminator($this)) {
shift $args->@*;
for my $parameter ($self->parameters->@*) {
my $argument = $parameter->parse($args,$stash);
$argpack->arguments->{$argument->name} = $argument;
}
if ($args->@*) {
Getopt::Dakkar::X::ExtraArgs->throw({
args => $args,
});
}
}
else {
my $next_parameter = shift @parameters
or last;
my $argument = $next_parameter->parse($args,$stash);
$argpack->arguments->{$argument->name} = $argument;
}
}
my $new_stash = $self->make_stash($argpack) // $stash;
my $op = $self->op;
$new_stash->$op($argpack);
return $new_stash;
}
sub _is_terminator($self,$arg) { return $arg eq q{--} }
sub _looks_like_option($self,$arg) { return $arg =~ /^-/ }
sub _match_option($self,$arg) {
for my $option ($self->options->@*) {
return $option if $option->match($arg);
}
return undef;
}
sub _match_subcommand($self,$arg) {
for my $subcommand ($self->subcommands->@*) {
return $subcommand if $subcommand->match($arg);
}
return undef;
}
sub match($self,$arg) {
return !! grep { $_ eq $arg } $self->matching_strings->@*;
}