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. 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. 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 =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 =head2 Plugins

21
revbank
View file

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