mirror of
https://github.com/denisidoro/navi
synced 2025-02-16 12:38:28 +00:00
Use dictionaries (#47)
In order to cleanup the code Related: https://medium.com/@den.isidoro/dictionaries-in-shell-scripts-61d34e1c91c6 Possibly fix #43 (but may bring other inconsistencies)
This commit is contained in:
parent
1b4e8ec60f
commit
9848d7b39a
10 changed files with 230 additions and 27 deletions
|
@ -3,16 +3,16 @@
|
||||||
# Print resource documentation
|
# Print resource documentation
|
||||||
kubectl explain <resource>
|
kubectl explain <resource>
|
||||||
|
|
||||||
# Get nodes (add option `-o wide` for details)
|
# Get nodes (add option '-o wide' for details)
|
||||||
kubectl get nodes
|
kubectl get nodes
|
||||||
|
|
||||||
# Get namespaces
|
# Get namespaces
|
||||||
kubectl get namespaces
|
kubectl get namespaces
|
||||||
|
|
||||||
# Get pods from namespace (add option `-o wide` for details)
|
# Get pods from namespace (add option '-o wide' for details)
|
||||||
kubectl get pods -n <namespace>
|
kubectl get pods -n <namespace>
|
||||||
|
|
||||||
# Get pods from all namespace (add option `-o wide` for details)
|
# Get pods from all namespace (add option '-o wide' for details)
|
||||||
kubectl get pods --all-namespaces
|
kubectl get pods --all-namespaces
|
||||||
|
|
||||||
# Get services from namespace
|
# Get services from namespace
|
||||||
|
|
15
src/arg.sh
15
src/arg.sh
|
@ -2,12 +2,11 @@
|
||||||
|
|
||||||
ARG_REGEX="<[0-9a-zA-Z_]+>"
|
ARG_REGEX="<[0-9a-zA-Z_]+>"
|
||||||
|
|
||||||
arg::fn() {
|
arg::dict() {
|
||||||
awk -F'---' '{print $1}'
|
local -r fn="$(awk -F'---' '{print $1}')"
|
||||||
}
|
local -r opts="$(awk -F'---' '{print $2}')"
|
||||||
|
|
||||||
arg::opts() {
|
dict::new fn "$fn" opts "$opts"
|
||||||
awk -F'---' '{print $2}'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
arg::interpolate() {
|
arg::interpolate() {
|
||||||
|
@ -30,10 +29,10 @@ arg::pick() {
|
||||||
|
|
||||||
local -r prefix="$ ${arg}:"
|
local -r prefix="$ ${arg}:"
|
||||||
local -r length="$(echo "$prefix" | str::length)"
|
local -r length="$(echo "$prefix" | str::length)"
|
||||||
local -r arg_description="$(grep "$prefix" "$cheat" | str::sub $((length + 1)))"
|
local -r arg_dict="$(grep "$prefix" "$cheat" | str::sub $((length + 1)) | arg::dict)"
|
||||||
|
|
||||||
local -r fn="$(echo "$arg_description" | arg::fn)"
|
local -r fn="$(echo "$arg_dict" | dict::get fn)"
|
||||||
local -r args_str="$(echo "$arg_description" | arg::opts | tr ' ' '\n' || echo "")"
|
local -r args_str="$(echo "$arg_dict" | dic::get opts | tr ' ' '\n' || echo "")"
|
||||||
local arg_name=""
|
local arg_name=""
|
||||||
|
|
||||||
for arg_str in $args_str; do
|
for arg_str in $args_str; do
|
||||||
|
|
|
@ -24,7 +24,7 @@ cheat::from_selection() {
|
||||||
local -r cheats="$1"
|
local -r cheats="$1"
|
||||||
local -r selection="$2"
|
local -r selection="$2"
|
||||||
|
|
||||||
local -r tags="$(echo "$selection" | selection::tags)"
|
local -r tags="$(echo "$selection" | dict::get tags)"
|
||||||
|
|
||||||
for cheat in $cheats; do
|
for cheat in $cheats; do
|
||||||
if grep -q "% $tags" "$cheat"; then
|
if grep -q "% $tags" "$cheat"; then
|
||||||
|
|
94
src/dict.sh
Normal file
94
src/dict.sh
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
dict::_post() {
|
||||||
|
sed -E 's/; /\\n/g' | awk 'NF > 0' | sort
|
||||||
|
}
|
||||||
|
|
||||||
|
dict::new() {
|
||||||
|
if [ $# = 0 ]; then
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
echo "" | dict::assoc "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dict::dissoc() {
|
||||||
|
local -r key="$1"
|
||||||
|
|
||||||
|
grep -Ev "^${key}[^:]*:" | dict::_post
|
||||||
|
}
|
||||||
|
|
||||||
|
dict::assoc() {
|
||||||
|
local -r key="${1:-}"
|
||||||
|
local -r value="${2:-}"
|
||||||
|
local -r input="$(cat)"
|
||||||
|
|
||||||
|
if [ -z $key ]; then
|
||||||
|
printf "$input" | dict::_post
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$input" ]; then
|
||||||
|
local -r base="$(printf "$input" | dict::dissoc "$key"); "
|
||||||
|
else
|
||||||
|
local -r base=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
shift 2
|
||||||
|
printf "${base}${key}: ${value}" | dict::_post | dict::assoc "$@" | dict::_post
|
||||||
|
}
|
||||||
|
|
||||||
|
dict::get() {
|
||||||
|
local -r key="$1"
|
||||||
|
|
||||||
|
local -r prefix="${key}[^:]*: "
|
||||||
|
local -r result="$(grep -E "^${prefix}")"
|
||||||
|
local -r matches="$(echo "$result" | wc -l || echo 0)"
|
||||||
|
|
||||||
|
if [ $matches -gt 1 ]; then
|
||||||
|
echo "$result"
|
||||||
|
else
|
||||||
|
echo "$result" | sed -E "s/${prefix}//"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dict::keys() {
|
||||||
|
grep -Eo '^[^:]+: ' | sed 's/: //g'
|
||||||
|
}
|
||||||
|
|
||||||
|
dict::values() {
|
||||||
|
awk -F':' '{$1=""; print $0}' | cut -c3-
|
||||||
|
}
|
||||||
|
|
||||||
|
dict::zipmap() {
|
||||||
|
IFS='\n'
|
||||||
|
|
||||||
|
local -r keys_str="$1"
|
||||||
|
local -r values_str="$2"
|
||||||
|
|
||||||
|
keys=()
|
||||||
|
values=()
|
||||||
|
for key in $keys_str; do
|
||||||
|
keys+=("$key")
|
||||||
|
done
|
||||||
|
for value in $values_str; do
|
||||||
|
values+=("$value")
|
||||||
|
done
|
||||||
|
|
||||||
|
for ((i=0; i<${#keys[@]}; ++i)); do
|
||||||
|
if [ -n "${keys[i]}" ]; then
|
||||||
|
echo "${keys[i]}: ${values[i]}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
dict::update() {
|
||||||
|
local -r key="$1"
|
||||||
|
local -r fn="$2"
|
||||||
|
local -r input="$(cat)"
|
||||||
|
|
||||||
|
local -r value="$(echo "$input" | dict::get "$key")"
|
||||||
|
local -r updated_value="$(eval "$fn" "$value")"
|
||||||
|
|
||||||
|
echo "$input" | dict::assoc "$key" "$updated_value"
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
source "${SCRIPT_DIR}/src/arg.sh"
|
source "${SCRIPT_DIR}/src/arg.sh"
|
||||||
source "${SCRIPT_DIR}/src/cheat.sh"
|
source "${SCRIPT_DIR}/src/cheat.sh"
|
||||||
|
source "${SCRIPT_DIR}/src/dict.sh"
|
||||||
source "${SCRIPT_DIR}/src/health.sh"
|
source "${SCRIPT_DIR}/src/health.sh"
|
||||||
source "${SCRIPT_DIR}/src/misc.sh"
|
source "${SCRIPT_DIR}/src/misc.sh"
|
||||||
source "${SCRIPT_DIR}/src/opts.sh"
|
source "${SCRIPT_DIR}/src/opts.sh"
|
||||||
|
@ -15,7 +16,7 @@ handler::main() {
|
||||||
local -r selection="$(ui::select "$cheats")"
|
local -r selection="$(ui::select "$cheats")"
|
||||||
local -r cheat="$(cheat::from_selection "$cheats" "$selection")"
|
local -r cheat="$(cheat::from_selection "$cheats" "$selection")"
|
||||||
[ -z "$cheat" ] && exit 67
|
[ -z "$cheat" ] && exit 67
|
||||||
local cmd="$(selection::command "$selection" "$cheat")"
|
local cmd="$(selection::dict "$selection" "$cheat")"
|
||||||
local arg value
|
local arg value
|
||||||
|
|
||||||
while $interpolation; do
|
while $interpolation; do
|
||||||
|
@ -47,7 +48,7 @@ handler::preview() {
|
||||||
local -r selection="$(echo "$query" | selection::standardize)"
|
local -r selection="$(echo "$query" | selection::standardize)"
|
||||||
local -r cheats="$(cheat::find)"
|
local -r cheats="$(cheat::find)"
|
||||||
local -r cheat="$(cheat::from_selection "$cheats" "$selection")"
|
local -r cheat="$(cheat::from_selection "$cheats" "$selection")"
|
||||||
[ -n "$cheat" ] && selection::command "$selection" "$cheat"
|
[ -n "$cheat" ] && selection::dict "$selection" "$cheat"
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
|
|
|
@ -6,26 +6,18 @@ selection::standardize() {
|
||||||
local -r tags="$(echo "$str" | awk -F'[' '{print $NF}' | tr -d ']')"
|
local -r tags="$(echo "$str" | awk -F'[' '{print $NF}' | tr -d ']')"
|
||||||
local -r core="$(echo "$str" | sed -e "s/ \[${tags}\]$//")"
|
local -r core="$(echo "$str" | sed -e "s/ \[${tags}\]$//")"
|
||||||
|
|
||||||
echo "${core}^${tags}"
|
dict::new core "$core" tags "$tags"
|
||||||
}
|
|
||||||
|
|
||||||
selection::core() {
|
|
||||||
cut -d'^' -f1
|
|
||||||
}
|
|
||||||
|
|
||||||
selection::tags() {
|
|
||||||
cut -d'^' -f2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selection::core_is_comment() {
|
selection::core_is_comment() {
|
||||||
grep -qE '^#'
|
grep -qE '^#'
|
||||||
}
|
}
|
||||||
|
|
||||||
selection::command() {
|
selection::dict() {
|
||||||
local -r selection="$1"
|
local -r selection="$1"
|
||||||
local -r cheat="$2"
|
local -r cheat="$2"
|
||||||
|
|
||||||
local -r core="$(echo $selection | selection::core)"
|
local -r core="$(echo "$selection" | dict::get core)"
|
||||||
|
|
||||||
if echo "$core" | selection::core_is_comment; then
|
if echo "$core" | selection::core_is_comment; then
|
||||||
grep "$core" "$cheat" -A999 \
|
grep "$core" "$cheat" -A999 \
|
||||||
|
|
|
@ -6,7 +6,8 @@ ui::pick() {
|
||||||
|
|
||||||
ui::select() {
|
ui::select() {
|
||||||
local -r cheats="$1"
|
local -r cheats="$1"
|
||||||
local -r script_path="$(which navi | head -n1 || echo "${SCRIPT_DIR}/navi")"
|
# local -r script_path="$(which navi | head -n1 || echo "${SCRIPT_DIR}/navi")"
|
||||||
|
local -r script_path="$(echo "${SCRIPT_DIR}/navi")"
|
||||||
local -r preview_cmd="echo \"{}\" | tr ' ' '^' | xargs -I% \"${script_path}\" preview %"
|
local -r preview_cmd="echo \"{}\" | tr ' ' '^' | xargs -I% \"${script_path}\" preview %"
|
||||||
|
|
||||||
local args=()
|
local args=()
|
||||||
|
|
10
test/core.sh
10
test/core.sh
|
@ -23,6 +23,16 @@ test::run() {
|
||||||
eval "$*" && test::success || test::fail
|
eval "$*" && test::success || test::fail
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test::equals() {
|
||||||
|
local -r actual="$(cat | tr -d '\n')"
|
||||||
|
local -r expected="$(echo "${1:-}" | tr -d '\n' | sed 's/\\n//g')"
|
||||||
|
|
||||||
|
if [[ "$actual" != "$expected" ]]; then
|
||||||
|
echo "Expected '${expected}' but got '${actual}'"
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
test::finish() {
|
test::finish() {
|
||||||
echo
|
echo
|
||||||
if [ $FAILED -gt 0 ]; then
|
if [ $FAILED -gt 0 ]; then
|
||||||
|
|
106
test/dict_test.sh
Normal file
106
test/dict_test.sh
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
inc() {
|
||||||
|
local -r x="$1"
|
||||||
|
echo $((x+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_assoc() {
|
||||||
|
dict::new \
|
||||||
|
| dict::assoc "foo" "42" \
|
||||||
|
| test::equals "foo: 42"
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_assoc_multiple() {
|
||||||
|
dict::new \
|
||||||
|
| dict::assoc "foo" "42" "bar" "5" \
|
||||||
|
| test::equals "bar: 5\nfoo: 42"
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_dissoc() {
|
||||||
|
dict::new \
|
||||||
|
| dict::assoc "foo" "42" "bar" "5" \
|
||||||
|
| dict::dissoc "bar" \
|
||||||
|
| test::equals "foo: 42"
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_assoc_again() {
|
||||||
|
dict::new \
|
||||||
|
| dict::assoc "foo" "42" \
|
||||||
|
| dict::assoc "foo" "42" \
|
||||||
|
| test::equals "foo: 42"
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_dissoc_nested() {
|
||||||
|
dict::new \
|
||||||
|
| dict::assoc "foo" "42" "bar.a" 5 "bar.b" 6 "baz" 63 \
|
||||||
|
| dict::dissoc "bar" \
|
||||||
|
| test::equals "baz: 63\nfoo: 42"
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_assoc_nested() {
|
||||||
|
dict::new \
|
||||||
|
| dict::assoc "foo" "42" "bar.a" 5 "bar.c" 7 "baz" 63 \
|
||||||
|
| dict::assoc "bar.b" 6 \
|
||||||
|
| test::equals "bar.a: 5\nbar.b: 6\nbar.c: 7\nbaz: 63\nfoo: 42"
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_get() {
|
||||||
|
dict::new \
|
||||||
|
| dict::assoc "foo" "42" \
|
||||||
|
| dict::get "foo" \
|
||||||
|
| test::equals "42"
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_get_nested() {
|
||||||
|
dict::new \
|
||||||
|
| dict::assoc "foo" "42" "bar.a" 5 "bar.b" 6 "baz" 63 \
|
||||||
|
| dict::get "bar.a" \
|
||||||
|
| test::equals "5"
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_get_dict() {
|
||||||
|
dict::new \
|
||||||
|
| dict::assoc "foo" "42" "bar.a" 5 "bar.b" 6 "baz" 63 \
|
||||||
|
| dict::get "bar" \
|
||||||
|
| test::equals "bar.a: 5\nbar.b: 6"
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_get_keys() {
|
||||||
|
dict::new \
|
||||||
|
| dict::assoc "foo" "42" "bar.a" 5 "bar.b" 6 "baz" 63 \
|
||||||
|
| dict::keys \
|
||||||
|
| test::equals "bar.a\nbar.b\nbaz\nfoo"
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_get_values() {
|
||||||
|
dict::new \
|
||||||
|
| dict::assoc "foo" "42" "bar.a" 5 "bar.b" 6 "baz" 63 \
|
||||||
|
| dict::values \
|
||||||
|
| test::equals "5\n6\n63\n42"
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_zipmap() {
|
||||||
|
dict::zipmap "key1\nkey2\nkey3" "value1\nvalue2\nvalue3" \
|
||||||
|
| test::equals "$(dict::new "key1" "value1" "key2" "value2" "key3" "value3")"
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_update() {
|
||||||
|
dict::new "foo" 42 "bar" 5 \
|
||||||
|
| dict::update "bar" inc \
|
||||||
|
| test::equals "$(dict::new "foo" 42 "bar" 6)"
|
||||||
|
}
|
||||||
|
|
||||||
|
test::run "We can assoc a value" dict_assoc
|
||||||
|
test::run "We can assoc multiple values" dict_assoc_multiple
|
||||||
|
test::run "We can assoc a nested value" dict_assoc_nested
|
||||||
|
test::run "We can dissoc a value" dict_dissoc
|
||||||
|
test::run "Associng the same value is a no-op" dict_assoc_again
|
||||||
|
test::run "Dissocing a key will replace all its subvalues" dict_dissoc_nested
|
||||||
|
test::run "We can get a value" dict_get
|
||||||
|
test::run "We can get a nested value" dict_get_nested
|
||||||
|
test::run "We can get a dictionary" dict_get_dict
|
||||||
|
test::run "We can get all keys" dict_get_keys
|
||||||
|
test::run "We can get all values" dict_get_values
|
||||||
|
test::run "We can get create a dict from a zipmap" dict_zipmap
|
||||||
|
test::run "We can update a value" dict_update
|
2
test/run
2
test/run
|
@ -4,7 +4,7 @@ set -euo pipefail
|
||||||
export SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
export SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
source "${SCRIPT_DIR}/test/core.sh"
|
source "${SCRIPT_DIR}/test/core.sh"
|
||||||
|
|
||||||
tests="$(find "$SCRIPT_DIR/test" -iname '*_test.sh')"
|
tests="$(find "$SCRIPT_DIR/test" -iname "*${1:-}*_test.sh")"
|
||||||
|
|
||||||
for test in $tests; do
|
for test in $tests; do
|
||||||
source "$test"
|
source "$test"
|
||||||
|
|
Loading…
Add table
Reference in a new issue