input: allow "abort" as input to a plugin

I can't imagine this to be important but throughout the years it's been
expected by users that "abort" can be quoted and passed to a plugin like
one that prints barcodes.

It's still not possible to pass a literal string `abort` to a follow-up
prompt, leaving this feature only available to advanced users who (hope
to) know what they're doing.
This commit is contained in:
Juerd Waalboer 2023-12-26 04:19:36 +01:00
parent 45f7ccbe28
commit abe0f21c6a
2 changed files with 20 additions and 9 deletions

View file

@ -28,15 +28,17 @@ RevBank is a user-interactive CLI, intended for use with a keyboard and a barcod
Most barcode scanners virtually press the I<Enter> key after each scan, and RevBank is made with this in mind: any command parameters are typically presented as follow-up prompts.
For advanced users, a more shell-like interface is provided: a command and its arguments can be given on a single line, separated by spaces. On the top-level prompt (i.e. not in follow-up prompts), the input is whitespace separated, and each of the "words" is added to a stack, from which subsequent prompts are fed. As long as there are words on the stack, the printing of further prompts is suppressed.
For advanced users, a more shell-like interface is provided: a command and its arguments can be given on a single line, separated by spaces. On the top-level prompt (i.e. not in follow-up prompts), the input is whitespace separated, and each of the terms is added to a stack, from which subsequent prompts are fed. At that level, terms can be quoted with C<'single'> or C<"double"> quotes, and C<\> escapes the subsequent character. As long as there are words on the stack, the printing of further prompts is suppressed.
There is no syntax for indicating the end of a command: every command has either a fixed number of arguments (follow-up questions), or its own specialized way to indicate the end of a variable length list.
Multiple commands on a single line can be separated with C<;>. This is required after a command that finalizes a transaction (like a bare username after adding products), or between a command that takes arguments and a command that follows it.
There is no syntax for indicating the end of a command in the simple mode. Every command has either a fixed number of arguments (follow-up questions), or its own specialized way to indicate the end of a variable length list.
Similarly, the end of the "list of products" is not indicated by syntax, but by entering a username. Or, more technically correct: every product id is a command, and so is every username. The product id command adds an entry to the cart, the username command finalizes the transaction and empties the cart.
=head3 abort
The string C<abort> is hard-coded and will always abort the current transaction (i.e. reset the global state (cart)). This is intentional as users always need a "way out", and C<abort> is unlikely to be a valid response to any prompt anyway. (Just generate that C<abort> barcode externally, instead of with your print-a-barcode plugin...)
The string C<abort> is hard-coded and will always abort the current transaction (i.e. reset the global state (cart)). This is intentional as users always need a "way out", and C<abort> is unlikely to be a valid response to any prompt anyway. (The "advanced" input method lets you quote it, like C<"abort">, although that is probably only useful for a print-a-barcode plugin...)
=head2 Plugins

21
revbank
View file

@ -57,14 +57,19 @@ sub split_input($input) {
while (
$input =~ m[
\G \s*
(?| ' ( (?: \\. | [^\\'] )* ) ' (?=\s|;|$)
| " ( (?: \\. | [^\\"] )* ) " (?=\s|;|$)
| ( (?: \\. | [^\\;'"\s] )+ ) (?=\s|;|$)
| (;)
(?| (') ( (?: \\. | [^\\'] )* ) ' (?=\s|;|$)
| (") ( (?: \\. | [^\\"] )* ) " (?=\s|;|$)
| () ( (?: \\. | [^\\;'"\s] )+ ) (?=\s|;|$)
| () (;)
)
]xg
) {
push @terms, $1;
push @terms, (
(not $1) && $2 eq ";" ? "\0SEPARATOR"
: (not $1) && $2 eq "abort" ? "\0ABORT"
: $1 && $2 eq "abort" ? "abort"
: $2
);
$pos = pos($input) || 0;
}
@ -225,18 +230,22 @@ OUTER: for (;;) {
redo PROMPT;
}
} else {
$input = "\0ABORT" if $input =~ /^\s*abort\s*$/;
@words = $input;
}
}
WORD: for (;;) {
redo PROMPT if not @words;
abort if grep $_ eq 'abort', @words;
abort if grep $_ eq "\0ABORT", @words;
my $origword = my $word = shift @words;
my @allwords = ($origword);
next WORD if $word eq "\0SEPARATOR";
abort if $method eq "command" and $word eq "abort"; # here, even when quoted
push @retry, $word;
ALL_PLUGINS: { PLUGIN: for my $plugin (@plugins) {