Skip to content

Commit a64c835

Browse files
committed
refactor: Improve error 'enums' to be more cohesive
1 parent b76f593 commit a64c835

14 files changed

+256
-153
lines changed

README.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,9 @@ bpm install
5656

5757
## TODO
5858
- error on invalid references (`type=object` in virtual object metadata, when it is referencing an array)
59-
- add tests for array in array (like object in object)
6059
- ensure error (for set primarily) if the virtual object references a variable that does not exist
6160
- "queried for X, but found existing object": print object in error (same with indexed arrays)
62-
- -p flag?
6361
- support `--pass-by-value`
6462
- check for circular references
65-
- unset all function
6663
- --reply-with-ref --reply-with-value
6764
- meta: 'unset REPLY' potential conflict with set -u

docs/errors.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,38 @@ In this context, value means either "member of object" or "index of array"
66

77
## Get
88

9-
- `ERROR_VALUE_NOT_FOUND`
9+
- `ERROR_NOT_FOUND`
1010
- Attempted to access a value that does not exist
1111
- `ERROR_VALUE_INCORRECT_TYPE`
1212
- Attempted to access a value that has the wrong type. This can happen if the query uses `.[30]` to get an array index, but an object exists instead. It can also happen if the user writes `get-string`, and it found a non-string value after evaluating the query. It can also happen if the user writes `set-string`, and the place to write the new value already has a value of a different type
13-
- `ERROR_INTERNAL_INVALID_VOBJ`
13+
- `ERROR_VOBJ_INVALID_TYPE`
1414
- The virtual object (a string that contains a reference to the real global object) had keys with unexpected values. You shouldn't get this error unless something has seriously gone wrong
15+
16+
# Handling
17+
18+
Because Bash often does the wrong thing silently, it's important to fail early when any inputs are unexpected. `bash-object` does this at every level
19+
20+
Below, if any of the assurances do not pass, `bash-object` will fail with a non-zero exit code. For some of these, `VERIFY_BASH_OBJECT` must be a variable
21+
22+
23+
1. Argument parsing
24+
25+
- Ensures the correct number of arguments are passed (when applicable)
26+
- Ensures the arguments are not empty (when applicable)
27+
- Ensures applicable arguments refers to real variables (ex. the root object in both set/get, and the final_value for set)
28+
- Ensure applicable arguments refer to variables of the same type (for set)
29+
30+
2. Query parsing
31+
32+
- Ensures the query has the correct form
33+
34+
3. Tree traversing
35+
36+
- Ensure type specified in the virtual object matches that of the referenced object
37+
- Ensure type implied in query string matches the one specified in the virtual object
38+
39+
4. Final operation (set/get)
40+
41+
- Ensure type specified in the virtual object matches that of the referenced object
42+
- Ensure type implied in query string matches the one specified in the virtual object
43+
- Ensure type specified in set/get matches the one specified in the virtual object

pkg/lib/cmd/bobject.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ bobject() {
2828
bash_object.traverse-set --pass-by-ref object "$@"
2929
;;
3030
*)
31-
bash_object.util.die 'ERROR_INVALID_ARGS' "Subcommand '$subcmd' not recognized"
31+
bash_object.util.die 'ERROR_ARGUMENTS_INVALID' "Subcommand '$subcmd' not recognized"
3232
return
3333
esac
3434
}

pkg/lib/parse.sh

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ bash_object.parse_filter() {
2222

2323
if [ "$flag_parser_type" = 'simple' ]; then
2424
if [ "${filter::1}" != . ]; then
25-
bash_object.util.die 'ERROR_INVALID_FILTER' 'Filter must begin with a dot'
25+
bash_object.util.die 'ERROR_FILTER_INVALID' 'Filter must begin with a dot'
2626
return
2727
fi
2828

@@ -36,6 +36,7 @@ bash_object.parse_filter() {
3636
done
3737
IFS="$old_ifs"
3838
elif [ "$flag_parser_type" = 'advanced' ]; then
39+
# TODO: scope
3940
declare char=
4041
declare mode='MODE_DEFAULT'
4142
declare -i PARSER_COLUMN_NUMBER=0
@@ -59,15 +60,15 @@ bash_object.parse_filter() {
5960
if [ "$char" = . ]; then
6061
mode='MODE_EXPECTING_BRACKET'
6162
else
62-
bash_object.util.die 'ERROR_INVALID_FILTER' 'Filter must begin with a dot'
63+
bash_object.util.die 'ERROR_FILTER_INVALID' 'Filter must begin with a dot'
6364
return
6465
fi
6566
;;
6667
MODE_BEFORE_DOT)
6768
if [ "$char" = . ]; then
6869
mode='MODE_EXPECTING_BRACKET'
6970
else
70-
bash_object.util.die 'ERROR_INVALID_FILTER' 'Each part in a filter must be deliminated by a dot'
71+
bash_object.util.die 'ERROR_FILTER_INVALID' 'Each part in a filter must be deliminated by a dot'
7172
return
7273
fi
7374
;;
@@ -77,7 +78,7 @@ bash_object.parse_filter() {
7778
elif [ "$char" = $'\n' ]; then
7879
return
7980
else
80-
bash_object.util.die 'ERROR_INVALID_FILTER' 'A dot MUST be followed by an opening bracket in this mode'
81+
bash_object.util.die 'ERROR_FILTER_INVALID' 'A dot MUST be followed by an opening bracket in this mode'
8182
return
8283
fi
8384
;;
@@ -87,7 +88,7 @@ bash_object.parse_filter() {
8788
if [ "$char" = \" ]; then
8889
mode='MODE_EXPECTING_STRING'
8990
elif [ "$char" = ']' ]; then
90-
bash_object.util.die 'ERROR_INVALID_FILTER' 'Key cannot be empty'
91+
bash_object.util.die 'ERROR_FILTER_INVALID' 'Key cannot be empty'
9192
return
9293
else
9394
case "$char" in
@@ -96,7 +97,7 @@ bash_object.parse_filter() {
9697
mode='MODE_EXPECTING_READ_NUMBER'
9798
;;
9899
*)
99-
bash_object.util.die 'ERROR_INVALID_FILTER' 'A number or opening quote must follow an open bracket'
100+
bash_object.util.die 'ERROR_FILTER_INVALID' 'A number or opening quote must follow an open bracket'
100101
return
101102
;;
102103
esac
@@ -107,14 +108,14 @@ bash_object.parse_filter() {
107108
mode='MODE_STRING_ESCAPE_SEQUENCE'
108109
elif [ "$char" = \" ]; then
109110
if [ -z "$reply" ]; then
110-
bash_object.util.die 'ERROR_INVALID_FILTER' 'Key cannot be empty'
111+
bash_object.util.die 'ERROR_FILTER_INVALID' 'Key cannot be empty'
111112
return
112113
fi
113114

114115
REPLIES+=("$reply")
115116
mode='MODE_EXPECTING_CLOSING_BRACKET'
116117
elif [ "$char" = $'\n' ]; then
117-
bash_object.util.die 'ERROR_INVALID_FILTER' 'Filter is not complete'
118+
bash_object.util.die 'ERROR_FILTER_INVALID' 'Filter is not complete'
118119
return
119120
else
120121
reply+="$char"
@@ -126,7 +127,7 @@ bash_object.parse_filter() {
126127
\") reply+=\" ;;
127128
']') reply+=']' ;;
128129
*)
129-
bash_object.util.die 'ERROR_INVALID_FILTER' "Escape sequence of '$char' not valid"
130+
bash_object.util.die 'ERROR_FILTER_INVALID' "Escape sequence of '$char' not valid"
130131
return
131132
;;
132133
esac
@@ -142,7 +143,7 @@ bash_object.parse_filter() {
142143
reply+="$char"
143144
;;
144145
*)
145-
bash_object.util.die 'ERROR_INVALID_FILTER' "Expecting number, found '$char'"
146+
bash_object.util.die 'ERROR_FILTER_INVALID' "Expecting number, found '$char'"
146147
return
147148
;;
148149
esac
@@ -152,14 +153,14 @@ bash_object.parse_filter() {
152153
if [ "$char" = ']' ]; then
153154
mode='MODE_BEFORE_DOT'
154155
else
155-
bash_object.util.die 'ERROR_INVALID_FILTER' 'Expected a closing bracket after the closing quotation mark'
156+
bash_object.util.die 'ERROR_FILTER_INVALID' 'Expected a closing bracket after the closing quotation mark'
156157
return
157158
fi
158159
;;
159160
esac
160161
done <<< "$filter"
161162
else
162-
bash_object.util.die 'ERROR_INVALID_ARGS' "Must pass either '--simple' or '--advanced'"
163+
bash_object.util.die 'ERROR_ARGUMENTS_INVALID' "Must pass either '--simple' or '--advanced'"
163164
return
164165
fi
165166
}

pkg/lib/traverse-get.sh

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,21 @@ bash_object.traverse-get() {
1414

1515
# Ensure correct number of arguments have been passed
1616
if (( $# != 3)); then
17-
bash_object.util.die 'ERROR_INVALID_ARGS' "Expected '3' arguments, but received '$#'"
17+
bash_object.util.die 'ERROR_ARGUMENTS_INVALID' "Expected '3' arguments, but received '$#'"
1818
return
1919
fi
2020

2121
# Ensure parameters are not empty
2222
if [ -z "$final_value_type" ]; then
23-
bash_object.util.die 'ERROR_INVALID_ARGS' "Positional parameter '1' is empty. Please check passed parameters"
23+
bash_object.util.die 'ERROR_ARGUMENTS_INVALID' "Positional parameter '1' is empty. Please check passed parameters"
2424
return
2525
fi
2626
if [ -z "$root_object_name" ]; then
27-
bash_object.util.die 'ERROR_INVALID_ARGS' "Positional parameter '2' is empty. Please check passed parameters"
27+
bash_object.util.die 'ERROR_ARGUMENTS_INVALID' "Positional parameter '2' is empty. Please check passed parameters"
2828
return
2929
fi
3030
if [ -z "$filter" ]; then
31-
bash_object.util.die 'ERROR_INVALID_ARGS' "Positional parameter '3' is empty. Please check passed parameters"
31+
bash_object.util.die 'ERROR_ARGUMENTS_INVALID' "Positional parameter '3' is empty. Please check passed parameters"
3232
return
3333
fi
3434

@@ -58,7 +58,7 @@ bash_object.traverse-get() {
5858

5959
# If 'key' is not a member of object or index of array, error
6060
if [ -z "${current_object["$key"]+x}" ]; then
61-
bash_object.util.die 'ERROR_VALUE_NOT_FOUND' "Key or index '$key' (filter index '$i') does not exist"
61+
bash_object.util.die 'ERROR_NOT_FOUND' "Key or index '$key' (filter index '$i') does not exist"
6262
return
6363
# If 'key' is a member of an object or index of array
6464
else
@@ -77,29 +77,69 @@ bash_object.traverse-get() {
7777
local -n current_object="$current_object_name"
7878

7979
if ((i+1 < ${#REPLIES[@]})); then
80+
# echo aa >&3
81+
# if [ -n "${VERIFY_BASH_OBJECT+x}" ]; then
82+
# echo jj >&3
83+
# fi
84+
8085
# TODO: test these internal invalid errors (error when type=array references object, etc.)
8186
# Do nothing (assuming the type is correct), we have already set 'current_object'
8287
# for the next iteration
8388
:
8489
# case "$vmd_dtype" in
8590
# object)
8691
# if [ "$is_index_of_array" = yes ]; then
87-
# bash_object.util.die 'ERROR_INTERNAL_INVALID_VOBJ' "Expected object, but reference to array was found"
92+
# bash_object.util.die 'ERROR_VOBJ_INVALID_TYPE' "Expected object, but reference to array was found"
8893
# return
8994
# fi
9095
# ;;
9196
# array)
9297
# if [ "$is_index_of_array" = no ]; then
93-
# bash_object.util.die 'ERROR_INTERNAL_INVALID_VOBJ' "Expected array, but reference to object was found"
98+
# bash_object.util.die 'ERROR_VOBJ_INVALID_TYPE' "Expected array, but reference to object was found"
9499
# return
95100
# fi
96101
# ;;
97102
# *)
98-
# bash_object.util.die 'ERROR_INTERNAL_INVALID_VOBJ' "Unexpected vmd_dtype '$vmd_dtype'"
103+
# bash_object.util.die 'ERROR_VOBJ_INVALID_TYPE' "Unexpected vmd_dtype '$vmd_dtype'"
99104
# return
100105
# ;;
101106
# esac
102107
elif ((i+1 == ${#REPLIES[@]})); then
108+
if [ -n "${VERIFY_BASH_OBJECT+x}" ]; then
109+
# Ensure the 'final_value' is the same type as specified by the user
110+
local current_object_type=
111+
if ! current_object_type="$(declare -p "$current_object_name" 2>/dev/null)"; then
112+
bash_object.util.die 'ERROR_INTERNAL' "The variable '$current_object_name' does not exist"
113+
return
114+
fi
115+
current_object_type="${current_object_type#declare -}"
116+
case "${current_object_type::1}" in
117+
A) current_object_type='object' ;;
118+
a) current_object_type='array' ;;
119+
-) current_object_type='string' ;;
120+
*) current_object_type='other' ;;
121+
esac
122+
123+
case "$vmd_dtype" in
124+
object)
125+
if [ "$current_object_type" != object ]; then
126+
bash_object.util.die 'ERROR_VOBJ_INCORRECT_TYPE' "Virtual object has a reference of type '$vmd_dtype', but when dereferencing, a variable of type '$current_object_type' was found"
127+
return
128+
fi
129+
;;
130+
array)
131+
if [ "$current_object_type" != array ]; then
132+
bash_object.util.die 'ERROR_VOBJ_INCORRECT_TYPE' "Virtual object has a reference of type '$vmd_dtype', but when dereferencing, a variable of type '$current_object_type' was found"
133+
return
134+
fi
135+
;;
136+
*)
137+
bash_object.util.die 'ERROR_VOBJ_INVALID_TYPE' "Unexpected vmd_dtype '$vmd_dtype'"
138+
return
139+
;;
140+
esac
141+
fi
142+
103143
# We are last element of query, return the object
104144
if [ "$final_value_type" = object ]; then
105145
case "$vmd_dtype" in
@@ -111,18 +151,18 @@ bash_object.traverse-get() {
111151
done
112152
;;
113153
array)
114-
bash_object.util.die 'ERROR_VALUE_INCORRECT_TYPE' 'Queried for object, but found existing array'
154+
bash_object.util.die 'ERROR_ARGUMENTS_INCORRECT_TYPE' 'Queried for object, but found existing array'
115155
return
116156
;;
117157
*)
118-
bash_object.util.die 'ERROR_INTERNAL_INVALID_VOBJ' "Unexpected vmd_dtype '$vmd_dtype'"
158+
bash_object.util.die 'ERROR_VOBJ_INVALID_TYPE' "Unexpected vmd_dtype '$vmd_dtype'"
119159
return
120160
;;
121161
esac
122162
elif [ "$final_value_type" = array ]; then
123163
case "$vmd_dtype" in
124164
object)
125-
bash_object.util.die 'ERROR_VALUE_INCORRECT_TYPE' 'Queried for array, but found existing object'
165+
bash_object.util.die 'ERROR_ARGUMENTS_INCORRECT_TYPE' 'Queried for array, but found existing object'
126166
return
127167
;;
128168
array)
@@ -131,26 +171,26 @@ bash_object.traverse-get() {
131171
REPLY=("${current_object[@]}")
132172
;;
133173
*)
134-
bash_object.util.die 'ERROR_INTERNAL_INVALID_VOBJ' "Unexpected vmd_dtype '$vmd_dtype'"
174+
bash_object.util.die 'ERROR_VOBJ_INVALID_TYPE' "Unexpected vmd_dtype '$vmd_dtype'"
135175
return
136176
;;
137177
esac
138178
elif [ "$final_value_type" = string ]; then
139179
case "$vmd_dtype" in
140180
object)
141-
bash_object.util.die 'ERROR_VALUE_INCORRECT_TYPE' 'Queried for string, but found existing object'
181+
bash_object.util.die 'ERROR_ARGUMENTS_INCORRECT_TYPE' 'Queried for string, but found existing object'
142182
return
143183
;;
144184
array)
145-
bash_object.util.die 'ERROR_VALUE_INCORRECT_TYPE' 'Queried for string, but found existing array'
185+
bash_object.util.die 'ERROR_ARGUMENTS_INCORRECT_TYPE' 'Queried for string, but found existing array'
146186
return
147187
;;
148188
*)
149-
bash_object.util.die 'ERROR_INTERNAL_INVALID_VOBJ' "Unexpected vmd_dtype '$vmd_dtype'"
189+
bash_object.util.die 'ERROR_VOBJ_INVALID_TYPE' "Unexpected vmd_dtype '$vmd_dtype'"
150190
return
151191
esac
152192
else
153-
bash_object.util.die 'ERROR_INTERNAL_INVALID_PARAM' "Unexpected final_value_type '$final_value_type'"
193+
bash_object.util.die 'ERROR_ARGUMENTS_INVALID_TYPE' "Unexpected final_value_type '$final_value_type'"
154194
return
155195
fi
156196
fi
@@ -169,10 +209,10 @@ bash_object.traverse-get() {
169209
elif ((i+1 == ${#REPLIES[@]})); then
170210
local value="${current_object["$key"]}"
171211
if [ "$final_value_type" = object ]; then
172-
bash_object.util.die 'ERROR_VALUE_INCORRECT_TYPE' "Queried for object, but found existing string '$value'"
212+
bash_object.util.die 'ERROR_ARGUMENTS_INCORRECT_TYPE' "Queried for $final_value_type, but found existing string '$value'"
173213
return
174214
elif [ "$final_value_type" = array ]; then
175-
bash_object.util.die 'ERROR_VALUE_INCORRECT_TYPE' "Queried for array, but found existing string '$value'"
215+
bash_object.util.die 'ERROR_ARGUMENTS_INCORRECT_TYPE' "Queried for $final_value_type, but found existing string '$value'"
176216
return
177217
elif [ "$final_value_type" = string ]; then
178218
# shellcheck disable=SC2178

0 commit comments

Comments
 (0)