]> git.corax.cc Git - toolbox/commitdiff
include/json: Introduce type specifiers
authorMatthias Kruk <m@m10k.eu>
Wed, 7 Jul 2021 00:18:03 +0000 (09:18 +0900)
committerMatthias Kruk <m@m10k.eu>
Wed, 7 Jul 2021 00:18:03 +0000 (09:18 +0900)
When constructing JSON objects and arrays, there are cases where the
json_object() and json_array() functions cannot correctly determine
the type of a variable (for example, "true" could be string or bool).
This commit introduces type specifiers that allow the user to declare
the type of a value that is passed to the json functions.
Specifiers are prepended to the value when passing it to the
json_object() json_array() functions. The following specifiers are
understood:

 - "s:" string
 - "i:" integer
 - "b:" boolean
 - "f:" float
 - "o:" object
 - "a:" array

The following is how the specifiers are passed.

 json_object "some_string"  "s:$some_string" \
             "some_integer" "i:$some_integer"

For backwards compatibility, the specifiers may be omitted. In that
case, a best-effort guess will be made. Omitting type specifiers will
make it impossible to pass certain values in strings.

include/json.sh

index b60da1d6fc11fd81263bf85352c8a1949a9b34e0..af81d18d1549175eee2cc92602dc1fd39a7e0c0d 100644 (file)
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 __init() {
-       if ! include "log"; then
+       if ! include "log" "is" "array"; then
                return 1
        fi
 
+       declare -gxir __json_type_integer=0
+       declare -gxir __json_type_string=1
+       declare -gxir __json_type_bool=2
+       declare -gxir __json_type_float=3
+       declare -gxir __json_type_object=4
+       declare -gxir __json_type_array=5
+
        return 0
 }
 
-json_object() {
-       local args=("$@")
+_json_guess_type() {
+       local input="$1"
+
+       local re_number
+       local re_float
+       local re_object
+       local re_array
+
+       re_number='^[-+]{,1}[0-9]+$'
+       re_float='^[-+]{,1}([0-9]*\.[0-9]+|[0-9]+\.[0-9]*)$'
+       re_object='^\{.*\}$'
+       re_array='^\[.*\]$'
+
+       if [[ "$input" =~ $re_number ]]; then
+               echo "$__json_type_integer"
+
+       elif [[ "$input" =~ $re_object ]]; then
+               echo "$__json_type_object"
 
-        local i
-        local nvps
+       elif [[ "$input" =~ $re_array ]]; then
+               echo "$__json_type_array"
+
+       elif [[ "$input" =~ $re_float ]]; then
+               echo "$__json_type_float"
+
+       elif [[ "$input" == "true" ]] ||
+            [[ "$input" == "false" ]]; then
+               echo "$__json_type_bool"
+
+       else
+               echo "$__json_type_string"
+       fi
+
+       return 0
+}
+
+_json_get_type() {
+       local input="$1"
+
+       declare -A type_map
+       local type_prefix
+       local type
+       local output
+
+       type_map["s:"]="$__json_type_string"
+       type_map["i:"]="$__json_type_integer"
+       type_map["b:"]="$__json_type_bool"
+       type_map["f:"]="$__json_type_float"
+       type_map["o:"]="$__json_type_object"
+       type_map["a:"]="$__json_type_array"
+
+       type_prefix="${input:0:2}"
+
+       if array_contains "$type_prefix" "${!type_map[@]}"; then
+               type="${type_map[$type_prefix]}"
+               output="${input:2}"
+       else
+               type=$(_json_guess_type "$input")
+               output="$input"
+       fi
+
+       echo "$output"
+       return "$type"
+}
 
-        nvps=0
+_json_print_value() {
+       local value="$1"
+
+       local value_raw
+       local output
+
+       value_raw=$(_json_get_type "$value")
+       case "$?" in
+               "$__json_type_integer")
+                       if ! output=$(printf '%d' "$value_raw"); then
+                               return 1
+                       fi
+                       ;;
+
+               "$__json_type_float")
+                       if ! output=$(printf '%f' "$value_raw"); then
+                               return 1
+                       fi
+                       ;;
+
+               "$__json_type_bool"|"$__json_type_object"|"$__json_type_array")
+                       if ! output=$(printf '%s' "$value_raw"); then
+                               return 1
+                       fi
+                       ;;
+
+               "$__json_type_string")
+                       if ! output=$(printf '"%s"' "$value_raw"); then
+                               return 1
+                       fi
+                       ;;
+
+               *)
+                       return 1
+                       ;;
+        esac
+
+       echo "$output"
+       return 0
+}
 
-        if (( ${#args[@]} % 2 != 0 )); then
-                log_error "Invalid number of arguments"
-                return 1
-        fi
+json_object() {
+       local args=("$@")
 
-        printf "{"
-        for (( i = 0; i < ${#args[@]}; i++ )); do
-                local name
-                local value
+       local i
+       local nvps
+       local output
 
-               local re_number
-               local re_object
-               local re_array
+       nvps=0
+       output="{"
 
-               re_number='^[0-9]+$'
-               re_object='^\{.*\}$'
-               re_array='^\[.*\]$'
+       if (( ${#args[@]} % 2 != 0 )); then
+               log_error "Invalid number of arguments"
+               return 1
+       fi
 
-                name="${args[$i]}"
-                ((i++))
-                value="${args[$i]}"
+       for (( i = 0; i < ${#args[@]}; i++ )); do
+               local name
+               local value
+               local value_raw
 
-                if [ -z "$name" ] || [ -z "$value" ]; then
-                        continue
-                fi
+               name="${args[$i]}"
+               ((i++))
+               value="${args[$i]}"
 
-                if (( nvps > 0 )); then
-                        printf ', '
-                fi
+               if [ -z "$name" ] || [ -z "$value" ]; then
+                       continue
+               fi
 
-                if [[ "$value" =~ $re_number ]]; then
-                        printf '"%s": %d' "$name" "$value"
+               if (( nvps > 0 )); then
+                       output+=", "
+               fi
 
-               elif [[ "$value" =~ $re_object ]] ||
-                    [[ "$value" =~ $re_array ]]; then
-                        printf '"%s": %s' "$name" "$value"
+               if ! value_raw=$(_json_print_value "$value"); then
+                       return 1
+               fi
 
-                else
-                        printf '"%s": "%s"' "$name" "$value"
-                fi
+               if ! output+=$(printf '"%s": %s' "$name" "$value_raw"); then
+                       return 1
+               fi
 
-                ((nvps++))
-        done
-        printf "}\n"
+               ((nvps++))
+       done
+       output+="}"
 
-        return 0
+       printf "%s\n" "$output"
+       return 0
 }
 
 json_object_get() {
@@ -99,18 +204,11 @@ json_object_get() {
 json_array() {
        local args=("$@")
 
+       local output
        local arg
        local n
 
-       local re_number
-       local re_object
-       local re_array
-
-       re_number='^[0-9]+$'
-       re_object='^\{.*\}$'
-       re_array='^\[.*\]$'
-
-       printf "["
+       output="["
        n=0
 
        for arg in "${args[@]}"; do
@@ -119,23 +217,18 @@ json_array() {
                fi
 
                if (( n > 0 )); then
-                       printf ", "
+                       output+=", "
                fi
 
-               if [[ "$arg" =~ $re_number ]]; then
-                       printf '%d' "$arg"
-
-               elif [[ "$arg" =~ $re_object ]] ||
-                    [[ "$arg" =~ $re_array ]]; then
-                       printf '%s' "$arg"
-
-               else
-                       printf '"%s"' "$arg"
+               if ! output+=$(_json_print_value "$arg"); then
+                       return 1
                fi
 
                ((n++))
        done
-       printf "]\n"
+       output+="]"
+
+       printf "%s\n" "$output"
 
        return 0
 }