Commit cffa56ed750ee1e7ca634e316464b0f07f23dce8
1 parent
0b4fbaaf6a
Added function to handle argument parsing.
Showing 2 changed files with 160 additions and 0 deletions Side-by-side Diff
antigen.zsh
... | ... | @@ -364,6 +364,118 @@ antigen () { |
364 | 364 | "antigen-$cmd" "$@" |
365 | 365 | } |
366 | 366 | |
367 | +-antigen-parse-args () { | |
368 | + # An argument parsing functionality to parse arguments the *antigen* way :). | |
369 | + # Takes one first argument (called spec), which dictates how to parse and | |
370 | + # the rest of the arguments are parsed. Outputs a piece of valid shell code | |
371 | + # that can be passed to `eval` inside a function which creates the arguments | |
372 | + # and their values as local variables. Suggested use is to set the defaults | |
373 | + # to all arguments first and then eval the output of this function. | |
374 | + | |
375 | + # Spec: Only long argument supported. No support for parsing short options. | |
376 | + # The spec must have two sections, separated by a `;`. | |
377 | + # '<positional-arguments>;<keyword-only-arguments>' | |
378 | + # Positional arguments are passed as just values, like `command a b`. | |
379 | + # Keyword arguments are passed as a `--name=value` pair, like `command | |
380 | + # --arg1=a --arg2=b`. | |
381 | + | |
382 | + # Each argument in the spec is separated by a `,`. Each keyword argument can | |
383 | + # end in a `:` to specifiy that this argument wants a value, otherwise it | |
384 | + # doesn't take a value. (The value in the output when the keyword argument | |
385 | + # doesn't have a `:` is `true`). | |
386 | + | |
387 | + # Arguments in either section can end with a `?` (should come after `:`, if | |
388 | + # both are present), means optional. FIXME: Not yet implemented. | |
389 | + | |
390 | + # See the test file, tests/arg-parser.t for (working) examples. | |
391 | + | |
392 | + local spec="$1" | |
393 | + shift | |
394 | + | |
395 | + local code='' | |
396 | + | |
397 | + --add-var () { | |
398 | + test -z "$code" || code="$code\n" | |
399 | + code="${code}local $1='$2'" | |
400 | + } | |
401 | + | |
402 | + local positional_args="$(echo "$spec" | cut -d\; -f1)" | |
403 | + local positional_args_count="$(echo $positional_args | | |
404 | + awk -F, '{print NF}')" | |
405 | + | |
406 | + # Set spec values based on the positional arguments. | |
407 | + local i=1 | |
408 | + while ! [[ -z $1 || $1 == --* ]]; do | |
409 | + | |
410 | + if (( $i > $positional_args_count )); then | |
411 | + echo "Only $positional_args_count positional arguments allowed." >&2 | |
412 | + echo "Found at least one more: '$1'" >&2 | |
413 | + return | |
414 | + fi | |
415 | + | |
416 | + local name_spec="$(echo "$positional_args" | cut -d, -f$i)" | |
417 | + local name="${${name_spec%\?}%:}" | |
418 | + local value="$1" | |
419 | + | |
420 | + --add-var $name "$value" | |
421 | + | |
422 | + shift | |
423 | + i=$(($i + 1)) | |
424 | + done | |
425 | + | |
426 | + local keyword_args="$(echo "$spec" | cut -d\; -f2 | tr , '\n')" | |
427 | + local keyword_args_count="$(echo $keyword_args | awk -F, '{print NF}')" | |
428 | + | |
429 | + # Set spec values from keyword arguments, if any. The remaining arguments | |
430 | + # are all assumed to be keyword arguments. | |
431 | + while [[ $1 == --* ]]; do | |
432 | + # Remove the `--` at the start. | |
433 | + local arg="${1#--}" | |
434 | + | |
435 | + # Get the argument name and value. | |
436 | + if [[ $arg != *=* ]]; then | |
437 | + local name="$arg" | |
438 | + local value='' | |
439 | + else | |
440 | + local name="${arg%\=*}" | |
441 | + local value="${arg#*=}" | |
442 | + fi | |
443 | + | |
444 | + # The specification for this argument, used for validations. | |
445 | + local arg_line="$(echo "$keyword_args" | grep -m1 "^$name:\??\?")" | |
446 | + | |
447 | + # Validate argument and value. | |
448 | + if [[ -z $arg_line ]]; then | |
449 | + # This argument is not known to us. | |
450 | + echo "Unknown argument '$name'." >&2 | |
451 | + return | |
452 | + | |
453 | + elif (echo "$arg_line" | grep -qm1 ':') && [[ -z $value ]]; then | |
454 | + # This argument needs a value, but is not provided. | |
455 | + echo "Required argument for '$name' not provided." >&2 | |
456 | + return | |
457 | + | |
458 | + elif (echo "$arg_line" | grep -vqm1 ':') && [[ ! -z $value ]]; then | |
459 | + # This argument doesn't need a value, but is provided. | |
460 | + echo "No argument required for '$name', but provided '$value'." >&2 | |
461 | + return | |
462 | + | |
463 | + fi | |
464 | + | |
465 | + if [[ -z $value ]]; then | |
466 | + value=true | |
467 | + fi | |
468 | + | |
469 | + --add-var "${name//-/_}" "$value" | |
470 | + shift | |
471 | + done | |
472 | + | |
473 | + echo "$code" | |
474 | + | |
475 | + unfunction -- --add-var | |
476 | + | |
477 | +} | |
478 | + | |
367 | 479 | # Echo the bundle specs as in the record. The first line is not echoed since it |
368 | 480 | # is a blank line. |
369 | 481 | -antigen-echo-record () { |
tests/arg-parser.t
... | ... | @@ -0,0 +1,48 @@ |
1 | +Helper alias. | |
2 | + | |
3 | + $ alias parse='-antigen-parse-args "url?,loc?;btype:?,no-local-clone?"' | |
4 | + | |
5 | +No arguments (since all are specified as optional). | |
6 | + | |
7 | + $ parse | |
8 | + (glob) | |
9 | + | |
10 | +One positional argument. | |
11 | + | |
12 | + $ parse name | |
13 | + local url='name' | |
14 | + | |
15 | +Two arguments. | |
16 | + | |
17 | + $ parse url location | |
18 | + local url='url' | |
19 | + local loc='location' | |
20 | + | |
21 | +Three arguments. | |
22 | + | |
23 | + $ parse url location crap | |
24 | + Only 2 positional arguments allowed. | |
25 | + Found at least one more: 'crap' | |
26 | + | |
27 | +Keywordo magic. | |
28 | + | |
29 | + $ parse url location --btype=1 --no-local-clone | |
30 | + local url='url' | |
31 | + local loc='location' | |
32 | + local btype='1' | |
33 | + local no_local_clone='true' | |
34 | + | |
35 | +Unknown keyword argument. | |
36 | + | |
37 | + $ parse --me=genius | |
38 | + Unknown argument 'me'. | |
39 | + | |
40 | +Missed value for keyword argument. | |
41 | + | |
42 | + $ parse --btype | |
43 | + Required argument for 'btype' not provided. | |
44 | + | |
45 | +Provide value for keyword argument, that shouldn't be there. | |
46 | + | |
47 | + $ parse --no-local-clone=yes | |
48 | + No argument required for 'no-local-clone', but provided 'yes'. |