diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-12 08:14:25 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-12 08:14:25 +0300 |
| commit | ebb307c24873863610815b59787de99e6b8c9fdb (patch) | |
| tree | aefcded06776155b65bf0bea96a9568259dab30d | |
| parent | aff74bffb00f1be36f218db52dd76fd1fd8549f9 (diff) | |
Consolidate CLI option specs
Task: am0
| -rwxr-xr-x | bin/shuriken | 154 | ||||
| -rw-r--r-- | src/lib/config.cli.source.sh | 50 | ||||
| -rwxr-xr-x | src/shuriken.sh | 104 | ||||
| -rwxr-xr-x | tests/cli.sh | 12 |
4 files changed, 155 insertions, 165 deletions
diff --git a/bin/shuriken b/bin/shuriken index 44f7bd9..fc0fe97 100755 --- a/bin/shuriken +++ b/bin/shuriken @@ -37,79 +37,37 @@ declare -ra CLI_CONFIG_OVERRIDE_TARGETS=( SYNC_DELETE TARBALL_INCLUDE ) -declare -Ar CLI_OPTION_KIND=( - [--config]=value - [--incoming]=value - [--dist]=value - [--template]=value - [--title]=value - [--height]=value - [--thumbheight]=value - [--maxpreviews]=value - [--image-jobs]=value - [--random-seed]=value - [--shuffle]=flag - [--no-shuffle]=flag - [--splash]=flag - [--no-splash]=flag - [--tarball]=flag - [--no-tarball]=flag - [--force]=flag - [--sync-delete]=flag - [--no-sync-delete]=flag - [--sync-destination]=value - [--verbose]=output - [--quiet]=output - [--version]=action - [--init]=action - [--clean]=action - [--generate]=action - [--refresh-splash]=action - [--sync]=action - [--dry-run]=action - [--print-config]=action -) -declare -Ar CLI_OPTION_TARGET=( - [--config]=SHURIKEN_CLI_CONFIG_FILE - [--verbose]=SHURIKEN_OUTPUT_MODE - [--quiet]=SHURIKEN_OUTPUT_MODE - [--force]=SHURIKEN_FORCE_GENERATE -) -declare -Ar CLI_OPTION_VALUE=( - [--shuffle]=yes - [--no-shuffle]=no - [--splash]=yes - [--no-splash]=no - [--tarball]=yes - [--no-tarball]=no - [--force]=yes - [--sync-delete]=yes - [--no-sync-delete]=no - [--verbose]=verbose - [--quiet]=quiet -) -declare -Ar CLI_OPTION_CONFIG_TARGET=( - [--incoming]=INCOMING_DIR - [--dist]=DIST_DIR - [--template]=TEMPLATE_DIR - [--title]=TITLE - [--height]=HEIGHT - [--thumbheight]=THUMBHEIGHT - [--maxpreviews]=MAXPREVIEWS - [--image-jobs]=IMAGE_JOBS - [--random-seed]=RANDOM_SEED - [--shuffle]=SHUFFLE - [--no-shuffle]=SHUFFLE - [--splash]=SPLASH_PAGE - [--no-splash]=SPLASH_PAGE - [--tarball]=TARBALL_INCLUDE - [--no-tarball]=TARBALL_INCLUDE - [--sync-delete]=SYNC_DELETE - [--no-sync-delete]=SYNC_DELETE -) -declare -Ar CLI_OPTION_ARGUMENT=( - [--config]=path - [--sync-destination]=destination +declare -Ar CLI_OPTION_SPEC=( + [--config]='kind=value target=SHURIKEN_CLI_CONFIG_FILE argument=path' + [--incoming]='kind=value config=INCOMING_DIR' + [--dist]='kind=value config=DIST_DIR' + [--template]='kind=value config=TEMPLATE_DIR' + [--title]='kind=value config=TITLE' + [--height]='kind=value config=HEIGHT' + [--thumbheight]='kind=value config=THUMBHEIGHT' + [--maxpreviews]='kind=value config=MAXPREVIEWS' + [--image-jobs]='kind=value config=IMAGE_JOBS' + [--random-seed]='kind=value config=RANDOM_SEED' + [--shuffle]='kind=flag value=yes config=SHUFFLE' + [--no-shuffle]='kind=flag value=no config=SHUFFLE' + [--splash]='kind=flag value=yes config=SPLASH_PAGE' + [--no-splash]='kind=flag value=no config=SPLASH_PAGE' + [--tarball]='kind=flag value=yes config=TARBALL_INCLUDE' + [--no-tarball]='kind=flag value=no config=TARBALL_INCLUDE' + [--force]='kind=flag value=yes target=SHURIKEN_FORCE_GENERATE' + [--sync-delete]='kind=flag value=yes config=SYNC_DELETE' + [--no-sync-delete]='kind=flag value=no config=SYNC_DELETE' + [--sync-destination]='kind=value append=SHURIKEN_CLI_SYNC_DESTINATIONS argument=destination' + [--verbose]='kind=output value=verbose target=SHURIKEN_OUTPUT_MODE' + [--quiet]='kind=output value=quiet target=SHURIKEN_OUTPUT_MODE' + [--version]='kind=action' + [--init]='kind=action' + [--clean]='kind=action' + [--generate]='kind=action' + [--refresh-splash]='kind=action' + [--sync]='kind=action' + [--dry-run]='kind=action' + [--print-config]='kind=action' ) SHURIKEN_SOURCE_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) @@ -2927,9 +2885,32 @@ validate_sync_config() { } # Inlined from src/lib/config.cli.source.sh +cli_option_property() { + local -r option="$1"; shift + local -r property="$1"; shift + local spec + local field + local -a fields=() + + spec="${CLI_OPTION_SPEC[$option]:-}" + read -r -a fields <<< "$spec" + + for field in "${fields[@]}"; do + case "$field" in + "$property="*) + printf '%s\n' "${field#*=}" + return + ;; + esac + done +} + option_value() { local -r option="$1"; shift - local -r argument="${CLI_OPTION_ARGUMENT[$option]:-value}" + local argument + + argument=$(cli_option_property "$option" argument) + argument="${argument:-value}" if (( $# == 0 )) || [ -z "$1" ]; then printf 'Error: %s requires a %s\n' "$option" "$argument" >&2 @@ -2964,37 +2945,46 @@ apply_cli_overrides() { set_cli_option_value() { local -r option="$1"; shift local -r value="$1"; shift + local append_target local config_target + local target + + append_target=$(cli_option_property "$option" append) + if [ -n "$append_target" ]; then + local -n append_ref="$append_target" - if [ "$option" = --sync-destination ]; then - SHURIKEN_CLI_SYNC_DESTINATIONS+=("$value") + append_ref+=("$value") SHURIKEN_CLI_HAS_CONFIG_OVERRIDES='yes' return fi - config_target="${CLI_OPTION_CONFIG_TARGET[$option]:-}" + config_target=$(cli_option_property "$option" config) if [ -n "$config_target" ]; then SHURIKEN_CLI_OVERRIDES["$config_target"]="$value" SHURIKEN_CLI_HAS_CONFIG_OVERRIDES='yes' return fi - printf -v "${CLI_OPTION_TARGET[$option]}" '%s' "$value" + target=$(cli_option_property "$option" target) + printf -v "$target" '%s' "$value" } set_cli_constant_option() { local -r option="$1"; shift - local -r value="${CLI_OPTION_VALUE[$option]}" local config_target + local target + local value - config_target="${CLI_OPTION_CONFIG_TARGET[$option]:-}" + value=$(cli_option_property "$option" value) + config_target=$(cli_option_property "$option" config) if [ -n "$config_target" ]; then SHURIKEN_CLI_OVERRIDES["$config_target"]="$value" SHURIKEN_CLI_HAS_CONFIG_OVERRIDES='yes' return fi - printf -v "${CLI_OPTION_TARGET[$option]}" '%s' "$value" + target=$(cli_option_property "$option" target) + printf -v "$target" '%s' "$value" } set_cli_action() { @@ -3017,7 +3007,7 @@ parse_cli_arguments() { option="$1" shift - option_kind="${CLI_OPTION_KIND[$option]:-}" + option_kind=$(cli_option_property "$option" kind) case "$option_kind" in value) option_arg=$(option_value "$option" "$@") diff --git a/src/lib/config.cli.source.sh b/src/lib/config.cli.source.sh index 90e4998..0d66c05 100644 --- a/src/lib/config.cli.source.sh +++ b/src/lib/config.cli.source.sh @@ -1,6 +1,29 @@ +cli_option_property() { + local -r option="$1"; shift + local -r property="$1"; shift + local spec + local field + local -a fields=() + + spec="${CLI_OPTION_SPEC[$option]:-}" + read -r -a fields <<< "$spec" + + for field in "${fields[@]}"; do + case "$field" in + "$property="*) + printf '%s\n' "${field#*=}" + return + ;; + esac + done +} + option_value() { local -r option="$1"; shift - local -r argument="${CLI_OPTION_ARGUMENT[$option]:-value}" + local argument + + argument=$(cli_option_property "$option" argument) + argument="${argument:-value}" if (( $# == 0 )) || [ -z "$1" ]; then printf 'Error: %s requires a %s\n' "$option" "$argument" >&2 @@ -35,37 +58,46 @@ apply_cli_overrides() { set_cli_option_value() { local -r option="$1"; shift local -r value="$1"; shift + local append_target local config_target + local target + + append_target=$(cli_option_property "$option" append) + if [ -n "$append_target" ]; then + local -n append_ref="$append_target" - if [ "$option" = --sync-destination ]; then - SHURIKEN_CLI_SYNC_DESTINATIONS+=("$value") + append_ref+=("$value") SHURIKEN_CLI_HAS_CONFIG_OVERRIDES='yes' return fi - config_target="${CLI_OPTION_CONFIG_TARGET[$option]:-}" + config_target=$(cli_option_property "$option" config) if [ -n "$config_target" ]; then SHURIKEN_CLI_OVERRIDES["$config_target"]="$value" SHURIKEN_CLI_HAS_CONFIG_OVERRIDES='yes' return fi - printf -v "${CLI_OPTION_TARGET[$option]}" '%s' "$value" + target=$(cli_option_property "$option" target) + printf -v "$target" '%s' "$value" } set_cli_constant_option() { local -r option="$1"; shift - local -r value="${CLI_OPTION_VALUE[$option]}" local config_target + local target + local value - config_target="${CLI_OPTION_CONFIG_TARGET[$option]:-}" + value=$(cli_option_property "$option" value) + config_target=$(cli_option_property "$option" config) if [ -n "$config_target" ]; then SHURIKEN_CLI_OVERRIDES["$config_target"]="$value" SHURIKEN_CLI_HAS_CONFIG_OVERRIDES='yes' return fi - printf -v "${CLI_OPTION_TARGET[$option]}" '%s' "$value" + target=$(cli_option_property "$option" target) + printf -v "$target" '%s' "$value" } set_cli_action() { @@ -88,7 +120,7 @@ parse_cli_arguments() { option="$1" shift - option_kind="${CLI_OPTION_KIND[$option]:-}" + option_kind=$(cli_option_property "$option" kind) case "$option_kind" in value) option_arg=$(option_value "$option" "$@") diff --git a/src/shuriken.sh b/src/shuriken.sh index 448e66b..a3d6f1f 100755 --- a/src/shuriken.sh +++ b/src/shuriken.sh @@ -37,79 +37,37 @@ declare -ra CLI_CONFIG_OVERRIDE_TARGETS=( SYNC_DELETE TARBALL_INCLUDE ) -declare -Ar CLI_OPTION_KIND=( - [--config]=value - [--incoming]=value - [--dist]=value - [--template]=value - [--title]=value - [--height]=value - [--thumbheight]=value - [--maxpreviews]=value - [--image-jobs]=value - [--random-seed]=value - [--shuffle]=flag - [--no-shuffle]=flag - [--splash]=flag - [--no-splash]=flag - [--tarball]=flag - [--no-tarball]=flag - [--force]=flag - [--sync-delete]=flag - [--no-sync-delete]=flag - [--sync-destination]=value - [--verbose]=output - [--quiet]=output - [--version]=action - [--init]=action - [--clean]=action - [--generate]=action - [--refresh-splash]=action - [--sync]=action - [--dry-run]=action - [--print-config]=action -) -declare -Ar CLI_OPTION_TARGET=( - [--config]=SHURIKEN_CLI_CONFIG_FILE - [--verbose]=SHURIKEN_OUTPUT_MODE - [--quiet]=SHURIKEN_OUTPUT_MODE - [--force]=SHURIKEN_FORCE_GENERATE -) -declare -Ar CLI_OPTION_VALUE=( - [--shuffle]=yes - [--no-shuffle]=no - [--splash]=yes - [--no-splash]=no - [--tarball]=yes - [--no-tarball]=no - [--force]=yes - [--sync-delete]=yes - [--no-sync-delete]=no - [--verbose]=verbose - [--quiet]=quiet -) -declare -Ar CLI_OPTION_CONFIG_TARGET=( - [--incoming]=INCOMING_DIR - [--dist]=DIST_DIR - [--template]=TEMPLATE_DIR - [--title]=TITLE - [--height]=HEIGHT - [--thumbheight]=THUMBHEIGHT - [--maxpreviews]=MAXPREVIEWS - [--image-jobs]=IMAGE_JOBS - [--random-seed]=RANDOM_SEED - [--shuffle]=SHUFFLE - [--no-shuffle]=SHUFFLE - [--splash]=SPLASH_PAGE - [--no-splash]=SPLASH_PAGE - [--tarball]=TARBALL_INCLUDE - [--no-tarball]=TARBALL_INCLUDE - [--sync-delete]=SYNC_DELETE - [--no-sync-delete]=SYNC_DELETE -) -declare -Ar CLI_OPTION_ARGUMENT=( - [--config]=path - [--sync-destination]=destination +declare -Ar CLI_OPTION_SPEC=( + [--config]='kind=value target=SHURIKEN_CLI_CONFIG_FILE argument=path' + [--incoming]='kind=value config=INCOMING_DIR' + [--dist]='kind=value config=DIST_DIR' + [--template]='kind=value config=TEMPLATE_DIR' + [--title]='kind=value config=TITLE' + [--height]='kind=value config=HEIGHT' + [--thumbheight]='kind=value config=THUMBHEIGHT' + [--maxpreviews]='kind=value config=MAXPREVIEWS' + [--image-jobs]='kind=value config=IMAGE_JOBS' + [--random-seed]='kind=value config=RANDOM_SEED' + [--shuffle]='kind=flag value=yes config=SHUFFLE' + [--no-shuffle]='kind=flag value=no config=SHUFFLE' + [--splash]='kind=flag value=yes config=SPLASH_PAGE' + [--no-splash]='kind=flag value=no config=SPLASH_PAGE' + [--tarball]='kind=flag value=yes config=TARBALL_INCLUDE' + [--no-tarball]='kind=flag value=no config=TARBALL_INCLUDE' + [--force]='kind=flag value=yes target=SHURIKEN_FORCE_GENERATE' + [--sync-delete]='kind=flag value=yes config=SYNC_DELETE' + [--no-sync-delete]='kind=flag value=no config=SYNC_DELETE' + [--sync-destination]='kind=value append=SHURIKEN_CLI_SYNC_DESTINATIONS argument=destination' + [--verbose]='kind=output value=verbose target=SHURIKEN_OUTPUT_MODE' + [--quiet]='kind=output value=quiet target=SHURIKEN_OUTPUT_MODE' + [--version]='kind=action' + [--init]='kind=action' + [--clean]='kind=action' + [--generate]='kind=action' + [--refresh-splash]='kind=action' + [--sync]='kind=action' + [--dry-run]='kind=action' + [--print-config]='kind=action' ) SHURIKEN_SOURCE_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) diff --git a/tests/cli.sh b/tests/cli.sh index 66fdf82..4999580 100755 --- a/tests/cli.sh +++ b/tests/cli.sh @@ -3780,7 +3780,13 @@ test_extra_args_fail() { } test_missing_option_values_fail() { + local expected_argument local option + local output + local -A value_option_arguments=( + [--config]=path + [--sync-destination]=destination + ) local -a value_options=( --config --incoming @@ -3796,7 +3802,11 @@ test_missing_option_values_fail() { ) for option in "${value_options[@]}"; do - test::assert_failure "$option requires a value" "$TEST_SHURIKEN" "$option" + expected_argument="${value_option_arguments[$option]:-value}" + output=$(test::capture_failure_output "$TEST_SHURIKEN" "$option") + test::assert_contains \ + "Error: $option requires a $expected_argument" \ + "$output" done } |
