From 7fce4340deb0b6337bbd7ef6071ee42529db222d Mon Sep 17 00:00:00 2001 From: dakkar Date: Fri, 13 Jul 2018 13:09:26 +0100 Subject: vaguely parse commands --- lib/Getopt/Dakkar/Role/Command.pm | 76 +++++++++++++++++++++++++++++++++++++++ lib/Getopt/Dakkar/Role/Piece.pm | 22 ++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 lib/Getopt/Dakkar/Role/Command.pm create mode 100644 lib/Getopt/Dakkar/Role/Piece.pm (limited to 'lib/Getopt/Dakkar/Role') 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); + } +} diff --git a/lib/Getopt/Dakkar/Role/Piece.pm b/lib/Getopt/Dakkar/Role/Piece.pm new file mode 100644 index 0000000..d981095 --- /dev/null +++ b/lib/Getopt/Dakkar/Role/Piece.pm @@ -0,0 +1,22 @@ +package Getopt::Dakkar::Role::Piece; +use Getopt::Dakkar::Style qw(role); +# VERSION +# ABSTRACT: a piece + +has class => ( is => 'ro', isa => ClassName, default => 'Getopt::Dakkar::Stash' ); +has object => ( is => 'ro', isa => Object ); +has op => ( is => 'ro', isa => Str|CodeRef ); + +sub make_stash($self,$argpack) { + if (my $o = $self->object) { return $o } + + my $class = use_module($self->class); + if (my $from_argpack = $class->can('new_from_argpack')) { + return $class->$from_argpack($argpack); + } + else { + return $class->new(); + } +} + +requires 'parse'; -- cgit v1.2.3