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'. |