#!/bin/bash set -e # Logging TASKS=() _last() { echo -n "${TASKS[0]}" } start() { for n in "${TASKS[@]}"; do echo -n ' ' done TASKS=( "$@" "${TASKS[@]}" ) echo -e "\x1b[0;1;34m@\x1b[0;34m $@\x1b[0m" } complete() { last="$(_last)" if [[ "$NOSHIFT" != 1 ]]; then TASKS=( "${TASKS[@]:1}" ) fi if [[ "$1" == "" ]] && [[ "$last" != "" ]]; then NOSHIFT=1 complete "$last" else for n in "${TASKS[@]}"; do echo -n ' ' done echo -e "\x1b[0;1;32m+\x1b[0;32m $@\x1b[0m" fi } err() { last="$(_last)" if [[ "$NOSHIFT" != 1 ]]; then TASKS=( "${TASKS[@]:1}" ) fi if [[ "$1" == "" ]] && [[ "$last" != "" ]]; then NOSHIFT=1 err "$last" else for n in "${TASKS[@]}"; do echo -n ' ' done echo -e "\x1b[0;1;31m!\x1b[0;31m $@\x1b[0m" 1>&2; fi } info() { echo -e "\x1b[0;1;94mi\x1b[0;94m $@\x1b[0m" 1>&2; } # Bash Array Joiner join_array() { printf -v __ "${1//%/%%}%s" "${@:2}" echo -n "${__:${#1}}" } # jq abstraction layer # Determines where to store the JSON - if the value is `IN_MEM`, then the value is kept in the JQ_BUFFER variable JSON_STORAGE_LOCATION="IN_MEM" JQ_BUFFER="" JQ_BUFFER_OUTPUT_FILE="" JQ_TRANSACTION=() JQ_IS_DOING_TRANSACTION=false JQ_POSITIONALS=() JQ_POSITIONAL_IDX=1 ARG=null jsettempstorage() { newstorage="${1:-"IN_MEM"}" if [[ "$newstorage" == "$JSON_STORAGE_LOCATION" ]]; then return 0; fi if [[ "$JSON_STORAGE_LOCATION" == "IN_MEM" ]]; then touch "$newstorage" <<< "$JQ_BUFFER" > "$newstorage" JQ_BUFFER="" elif [[ "$newstorage" == "IN_MEM" ]]; then JQ_BUFFER="$(<"$JSON_STORAGE_LOCATION")" rm "$JSON_STORAGE_LOCATION" else mv "$JSON_STORAGE_LOCATION" "$newstorage" fi JSON_STORAGE_LOCATION="$newstorage" } # Reads the storage buffer jreadbuffer() { if [[ "$JSON_STORAGE_LOCATION" == "IN_MEM" ]]; then echo -n "$JQ_BUFFER" else cat "$JSON_STORAGE_LOCATION" fi } # Writes the storage buffer jwritebuffer() { if [[ "$JSON_STORAGE_LOCATION" == "IN_MEM" ]]; then JQ_BUFFER="$@" else echo -n "$@" > "$JSON_STORAGE_LOCATION" fi } # Creates a new jq blank file jnew() { JQ_BUFFER_OUTPUT_FILE="$(realpath "${1:-"/dev/null"}")" jwritebuffer "${2:-"{}"}" } # Loads an existing jq file, falling back to creating a blank one if it doesnt exist jload() { JQ_BUFFER_OUTPUT_FILE="$(realpath "${1:-"/dev/null"}")" if [[ -f "$JQ_BUFFER_OUTPUT_FILE" ]]; then jwritebuffer "$(cat $JQ_BUFFER_OUTPUT_FILE)" else jwritebuffer "${2:-"{}"}" fi } # SQL-like Transaction Logic jtransaction() { if [[ "$JQ_IS_DOING_TRANSACTION" == "true" ]]; then err "Already performing transaction!" return 1 fi JQ_IS_DOING_TRANSACTION=true JQ_TRANSACTION=() } jabort() { if [[ "$JQ_IS_DOING_TRANSACTION" == "false" ]]; then err "No pending transaction!" return 1 fi JQ_IS_DOING_TRANSACTION=false JQ_TRANSACTION=() } jcommit() { if [[ "$JQ_IS_DOING_TRANSACTION" == "false" ]]; then err "No pending transaction!" return 1 fi JQ_IS_DOING_TRANSACTION=false # Incase it's empty: JQ_TRANSACTION+=( "." ) # As we are no longer in a transaction, jrun will run the pipe-delimited query jrun "$(join_array ' | ' "${JQ_TRANSACTION[@]}")" # We can, after running, clear the transaction JQ_TRANSACTION=() } # Loads a string that jq can positionally access later jsetasset() { ARG="(\$ARGS.positional[$JQ_POSITIONAL_IDX] | @base64d)" JQ_POSITIONALS+=( "$(base64 -w 0 <<< "$@")" ); ((JQ_POSITIONAL_IDX=JQ_POSITIONAL_IDX+1)) } jgetassetref() { echo -n "$ARG" ARG=null } # Perform a jq write operation jrun() { if [[ "$JQ_IS_DOING_TRANSACTION" == "true" ]]; then # If we're in a transaction, just push it to the list JQ_TRANSACTION+=( "($@)" ) else # Otherwise, perform the operation and write to the buffer if [[ "$JSON_STORAGE_LOCATION" == "IN_MEM" ]]; then JQ_BUFFER="$(jq -Mc "$@" --args owo ${JQ_POSITIONALS[@]} <<< "$JQ_BUFFER")" else jq -Mc "$@" --args owo ${JQ_POSITIONALS[@]} < "$JSON_STORAGE_LOCATION" > "$JSON_STORAGE_LOCATION~" mv "$JSON_STORAGE_LOCATION~" "$JSON_STORAGE_LOCATION" fi JQ_POSITIONALS=() JQ_POSITIONAL_IDX=1 fi } # Perform a jq write operation using a temp file _jrun_file() { if [[ "$JQ_IS_DOING_TRANSACTION" == "true" ]]; then # Does not work for transactions err "Cannot use _jrun_file in transaction!"; return 1; else # Otherwise, perform the operation and write to the buffer if [[ "$JSON_STORAGE_LOCATION" == "IN_MEM" ]]; then echo -n "$@" > /tmp/.operation.jq JQ_BUFFER="$(jq -Mcf /tmp/.operation.jq --args owo ${JQ_POSITIONALS[@]} <<< "$JQ_BUFFER")" rm /tmp/.operation.jq else echo -n "$@" > /tmp/.operation.jq jq -Mcf /tmp/.operation.jq --args owo ${JQ_POSITIONALS[@]} < "$JSON_STORAGE_LOCATION" > "$JSON_STORAGE_LOCATION~" rm /tmp/.operation.jq mv "$JSON_STORAGE_LOCATION~" "$JSON_STORAGE_LOCATION" fi JQ_POSITIONALS=() JQ_POSITIONAL_IDX=1 fi } jsave() { if [[ "$JQ_IS_DOING_TRANSACTION" == "true" ]]; then err "There is a pending transaction." return 1 fi jreadbuffer | jq -M . > "${1:-"$JQ_BUFFER_OUTPUT_FILE"}" } escape_str() { jq '.=$ARGS.positional[0]' --args "$@" -M <<< '""' }