summaryrefslogtreecommitdiff
path: root/lib/Getopt/Dakkar/Role/Command.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Getopt/Dakkar/Role/Command.pm')
-rw-r--r--lib/Getopt/Dakkar/Role/Command.pm76
1 files changed, 76 insertions, 0 deletions
diff --git a/lib/Getopt/Dakkar/Role/Command.pm b/lib/Getopt/Dakkar/Role/Command.pm
new file mode 100644
index 0000000..b96c410
--- /dev/null
+++ b/lib/Getopt/Dakkar/Role/Command.pm
@@ -0,0 +1,76 @@
+package Getopt::Dakkar::Role::Command;
+use Getopt::Dakkar::Style qw(role);
+with 'Getopt::Dakkar::Role::Piece';
+# VERSION
+# ABSTRACT: a command
+
+has options => ( is => 'ro', isa => ArrayRef, default => sub { [] } );
+has parameters => ( is => 'ro', isa => ArrayRef, default => sub { [] } );
+has subcommands => ( is => 'ro', isa => ArrayRef, default => sub { [] } );
+
+sub parse($self,$args=undef) {
+ my $stash = $self->_parse($args);
+ if (my $op = $self->op) {
+ $stash->$op();
+ }
+}
+
+sub _parse($self,$args,$stash=undef) {
+ my $argpack = Getopt::Dakkar::ArgPack->new({getopt=>$self});
+ while ($args->@*) {
+ my $this = $args->[0];
+
+ if ($self->_looks_like_option($this)
+ and not $self->_is_terminator($this)) {
+ if (my $match = $self->_match_option($this)) {
+ # ->parse will remove $this (or parts of
+ # it, if it's a bundle) and shift its own arguments
+ # out of $args
+ my $option = $match->parse($args,$self);
+ $argpack->options->{$match->name} = $option;
+ }
+ else {
+ Getopt::Dakkar::X::BadOption->throw({
+ input => $this,
+ });
+ }
+ }
+ elsif (my $match = $self->_match_subcommand($this)) {
+ shift $args->@*;
+ $stash //= $self->_make_stash($argpack);
+ my $op = $self->op;
+ $stash->$op($argpack);
+ $match->_parse($args,$stash);
+ }
+ else {
+ shift $args->@* if $self->_is_terminator($this);
+ for my $parameter ($self->parameters->@*) {
+ # ->parse will remove the argument(s) from $args
+ my $argument = $parameter->parse($args,$self);
+ $argpack->arguments->{$argument->name} = $argument;
+ }
+
+ # if we've parsed all arguments, and there's still stuff
+ # in the input, something is wrong: complain
+ if ($args->@*) {
+ Getopt::Dakkar::X::ExtraArgs->throw({
+ args => $args,
+ });
+ }
+ }
+ }
+ return $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);
+ }
+}
+sub _match_subcommand($self,$arg) {
+ for my $subcommand ($self->subcommands->@*) {
+ return $subcommand if $subcommand->match($arg);
+ }
+}