summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-12 08:14:25 +0300
committerPaul Buetow <paul@buetow.org>2026-06-12 08:14:25 +0300
commitebb307c24873863610815b59787de99e6b8c9fdb (patch)
treeaefcded06776155b65bf0bea96a9568259dab30d
parentaff74bffb00f1be36f218db52dd76fd1fd8549f9 (diff)
Consolidate CLI option specs
Task: am0
-rwxr-xr-xbin/shuriken154
-rw-r--r--src/lib/config.cli.source.sh50
-rwxr-xr-xsrc/shuriken.sh104
-rwxr-xr-xtests/cli.sh12
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
}