feat(package): hemar: json_escape()
This commit is contained in:
1
package/hemar/.gitignore
vendored
Normal file
1
package/hemar/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SUMMARY.md
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
grammar Hemar;
|
|
||||||
|
|
||||||
// ----------------- parser rules -----------------
|
|
||||||
|
|
||||||
hemar: elements? EOF ;
|
|
||||||
|
|
||||||
elements: element+ ;
|
|
||||||
|
|
||||||
element
|
|
||||||
: segment
|
|
||||||
| interpoltion
|
|
||||||
;
|
|
||||||
|
|
||||||
segment : for elements? end ;
|
|
||||||
|
|
||||||
for : 'for' 'in' ;
|
|
||||||
|
|
||||||
end: 'end' ;
|
|
||||||
|
|
||||||
interpoltion : 'mcha' ;
|
|
||||||
|
|
||||||
OPEN : '{[' ;
|
|
||||||
CLOSE : ']}' ;
|
|
||||||
|
|
||||||
WS : [ \t\n\r]+ -> skip ;
|
|
||||||
LEADING_TEXT : { getCharPositionInLine() == 0 }? (~'{'|'{'~'[')* OPEN -> skip;
|
|
||||||
MIDLE_TEXT : CLOSE (~'{'|'{'~'[')* OPEN -> skip;
|
|
||||||
ENDING_TEXT : CLOSE (~'{'|'{'~'[')* EOF -> skip ;
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
lexer grammar HemarLexer;
|
|
||||||
|
|
||||||
// ---------- default mode: plain text ----------
|
|
||||||
|
|
||||||
// Everything that is not the start of "{[" is TEXT
|
|
||||||
TEXT
|
|
||||||
: ( ~'{' | '{' ~'[' )+
|
|
||||||
;
|
|
||||||
|
|
||||||
// When we see "{[", emit LeftBrace and enter TAG mode
|
|
||||||
LeftBrace
|
|
||||||
: '{[' -> pushMode(TAG)
|
|
||||||
;
|
|
||||||
|
|
||||||
// skip whitespace in plain text if you want
|
|
||||||
SKIP_WS
|
|
||||||
: [ \t\r\n]+ -> skip
|
|
||||||
;
|
|
||||||
|
|
||||||
// ---------- TAG mode: inside {[ ... ]} ----------
|
|
||||||
|
|
||||||
mode TAG;
|
|
||||||
|
|
||||||
fragment WS: [ \t\r\n] ;
|
|
||||||
|
|
||||||
For : 'for';
|
|
||||||
In : 'in';
|
|
||||||
End : 'end';
|
|
||||||
|
|
||||||
// identifier inside tag
|
|
||||||
Path
|
|
||||||
: String
|
|
||||||
| String '.' Path
|
|
||||||
;
|
|
||||||
|
|
||||||
String
|
|
||||||
: ( ~[.\] \t\r\n] | ']' ~[}. \t\r\n] )+
|
|
||||||
| '"' ( ~'"' | '\\' '"' )+ '"'
|
|
||||||
;
|
|
||||||
|
|
||||||
// closing "]}": emit RightBrace and go back to default mode
|
|
||||||
RightBrace
|
|
||||||
: ']}' -> popMode
|
|
||||||
;
|
|
||||||
|
|
||||||
// skip whitespace inside tag
|
|
||||||
SKIP_TAG_WS
|
|
||||||
: WS+ -> skip
|
|
||||||
;
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
parser grammar HemarParser;
|
|
||||||
|
|
||||||
options { tokenVocab=HemarLexer; }
|
|
||||||
|
|
||||||
hemar : element*? EOF ;
|
|
||||||
|
|
||||||
element
|
|
||||||
: TEXT
|
|
||||||
| segment
|
|
||||||
| interpoltion
|
|
||||||
;
|
|
||||||
|
|
||||||
segment : for element*? end;
|
|
||||||
|
|
||||||
for : LeftBrace For Path In Path RightBrace;
|
|
||||||
end : LeftBrace End RightBrace;
|
|
||||||
|
|
||||||
interpoltion : LeftBrace Path RightBrace;
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
grammar Hemar;
|
|
||||||
|
|
||||||
// ----------------- parser rules -----------------
|
|
||||||
|
|
||||||
hemar
|
|
||||||
: elements? EOF
|
|
||||||
;
|
|
||||||
|
|
||||||
elements
|
|
||||||
: element+
|
|
||||||
;
|
|
||||||
|
|
||||||
element
|
|
||||||
: tag
|
|
||||||
| TEXT
|
|
||||||
;
|
|
||||||
|
|
||||||
// tag
|
|
||||||
tag
|
|
||||||
: OPEN path CLOSE
|
|
||||||
| OPEN loopStatement CLOSE
|
|
||||||
| OPEN includeHeader CLOSE
|
|
||||||
| OPEN 'end' CLOSE
|
|
||||||
| OPEN function CLOSE
|
|
||||||
| OPEN OPEN CLOSE // literal "{[" output
|
|
||||||
;
|
|
||||||
|
|
||||||
// loop tag: "for" string "in" path
|
|
||||||
loopStatement
|
|
||||||
: 'for' STRING 'in' path
|
|
||||||
;
|
|
||||||
|
|
||||||
// include tag: "include" path
|
|
||||||
includeHeader
|
|
||||||
: 'include' path
|
|
||||||
;
|
|
||||||
|
|
||||||
// function tag
|
|
||||||
function
|
|
||||||
: 'compute' language functionBody? // "compute" language body
|
|
||||||
| 'compute' '-' functionBody? // "compute" - body
|
|
||||||
;
|
|
||||||
|
|
||||||
language
|
|
||||||
: 'dash'
|
|
||||||
| 'plpgsql'
|
|
||||||
;
|
|
||||||
|
|
||||||
// everything up to (but not including) "]}"
|
|
||||||
// (raw body, including "{[" etc, at *token* level)
|
|
||||||
functionBody
|
|
||||||
: ( ~CLOSE )*
|
|
||||||
;
|
|
||||||
|
|
||||||
// path
|
|
||||||
path
|
|
||||||
: '.'
|
|
||||||
| segmentedPath
|
|
||||||
;
|
|
||||||
|
|
||||||
segmentedPath
|
|
||||||
: segment ('.' segment)*
|
|
||||||
;
|
|
||||||
|
|
||||||
segment
|
|
||||||
: STRING
|
|
||||||
| index
|
|
||||||
;
|
|
||||||
|
|
||||||
// index: \0 .. \9, \1.. \9\d*, and negative forms
|
|
||||||
index
|
|
||||||
: '\\' DIGIT
|
|
||||||
| '\\' ONENINE DIGITS?
|
|
||||||
| '\\' '-' DIGIT
|
|
||||||
| '\\' '-' ONENINE DIGITS?
|
|
||||||
;
|
|
||||||
|
|
||||||
// ----------------- lexer rules -----------------
|
|
||||||
|
|
||||||
OPEN : '{[';
|
|
||||||
CLOSE : ']}';
|
|
||||||
|
|
||||||
// text outside tags: anything except the "{[" sequence
|
|
||||||
TEXT
|
|
||||||
: TEXT_CHAR+
|
|
||||||
;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Strings used in paths/loop variables:
|
|
||||||
* "..." with escapes similar to your spec.
|
|
||||||
*/
|
|
||||||
STRING
|
|
||||||
: '"' ( ESC | STRING_CHAR )* '"'
|
|
||||||
;
|
|
||||||
|
|
||||||
fragment STRING_CHAR
|
|
||||||
: ~["\\\r\n]
|
|
||||||
;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Escapes:
|
|
||||||
* . (literal dot)
|
|
||||||
* ]} (literal "]}") -- note this is two chars after '\'
|
|
||||||
* " \"
|
|
||||||
* \ \\
|
|
||||||
* / \/
|
|
||||||
* b f n r t
|
|
||||||
* uXXXX (hex)
|
|
||||||
* whitespace after backslash (your ws-in-escape)
|
|
||||||
*/
|
|
||||||
fragment ESC
|
|
||||||
: '\\'
|
|
||||||
(
|
|
||||||
'.'
|
|
||||||
| ']}'
|
|
||||||
| '"'
|
|
||||||
| '\\'
|
|
||||||
| '/'
|
|
||||||
| 'b'
|
|
||||||
| 'f'
|
|
||||||
| 'n'
|
|
||||||
| 'r'
|
|
||||||
| 't'
|
|
||||||
| 'u' HEX HEX HEX HEX
|
|
||||||
| WS_CHAR
|
|
||||||
)
|
|
||||||
;
|
|
||||||
|
|
||||||
// digits / hex
|
|
||||||
DIGITS : DIGIT+ ;
|
|
||||||
DIGIT : [0-9] ;
|
|
||||||
ONENINE: [1-9] ;
|
|
||||||
HEX : [0-9a-fA-F] ;
|
|
||||||
|
|
||||||
// whitespace for normal lexing
|
|
||||||
WS
|
|
||||||
: [ \t\r\n]+ -> skip
|
|
||||||
;
|
|
||||||
|
|
||||||
// whitespace used inside escapes
|
|
||||||
fragment WS_CHAR
|
|
||||||
: [ \t\r\n]
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
fragment TEXT_CHAR
|
|
||||||
: ~'{' // any except '{'
|
|
||||||
| '{' ~'[' // '{' only if not starting OPEN
|
|
||||||
;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
{ stdenv, symlinkJoin, jre, antlr4, runtimeShell, jdk }:
|
|
||||||
|
|
||||||
let
|
|
||||||
hemar-grammar = stdenv.mkDerivation {
|
|
||||||
pname = "hemar-grammar";
|
|
||||||
version = "0.1.0";
|
|
||||||
|
|
||||||
src = ./.; # directory with Hemar.g4
|
|
||||||
|
|
||||||
nativeBuildInputs = [
|
|
||||||
antlr4
|
|
||||||
jdk
|
|
||||||
];
|
|
||||||
|
|
||||||
buildPhase = ''
|
|
||||||
antlr4 HemarLexer.g4 HemarParser.g4
|
|
||||||
javac *.java
|
|
||||||
'';
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
mkdir -p "$out/lib" "$out/bin"
|
|
||||||
cp *.class *.tokens "$out/lib"
|
|
||||||
|
|
||||||
cat > "$out/bin/hemar-grammar" <<EOF
|
|
||||||
#!${runtimeShell}
|
|
||||||
CLASSPATH="$out/lib:${antlr4}/share/java/*"
|
|
||||||
exec ${jre}/bin/java -cp "\$CLASSPATH" org.antlr.v4.gui.TestRig Hemar hemar "\$@"
|
|
||||||
EOF
|
|
||||||
chmod +x "$out/bin/hemar-grammar"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
in
|
|
||||||
symlinkJoin {
|
|
||||||
name = "hemar-grammar";
|
|
||||||
paths = [ hemar-grammar ];
|
|
||||||
}
|
|
||||||
@@ -6,19 +6,6 @@ let
|
|||||||
"nounset"
|
"nounset"
|
||||||
];
|
];
|
||||||
|
|
||||||
test = hectic.writeShellApplication {
|
|
||||||
inherit shell bashOptions;
|
|
||||||
name = "hemar-test";
|
|
||||||
runtimeInputs = [ ];
|
|
||||||
|
|
||||||
text = ''
|
|
||||||
# shellcheck disable=SC2034
|
|
||||||
WORKSPACE=${./.}
|
|
||||||
${builtins.readFile hectic.helpers.posix-shell.log}
|
|
||||||
${builtins.readFile ./test.sh}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
hemar = hectic.writeShellApplication {
|
hemar = hectic.writeShellApplication {
|
||||||
inherit shell bashOptions;
|
inherit shell bashOptions;
|
||||||
name = "hemar";
|
name = "hemar";
|
||||||
@@ -34,5 +21,5 @@ let
|
|||||||
in
|
in
|
||||||
symlinkJoin {
|
symlinkJoin {
|
||||||
name = "hemar";
|
name = "hemar";
|
||||||
paths = [ hemar test ];
|
paths = [ hemar ];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#!/bin/dash
|
#!/bin/dash
|
||||||
|
|
||||||
log notice "running"
|
|
||||||
|
|
||||||
# segmented-path
|
# segmented-path
|
||||||
# segment
|
# segment
|
||||||
# Syntax scheme:
|
# Syntax scheme:
|
||||||
@@ -97,57 +95,45 @@ log notice "running"
|
|||||||
# '{' '0020' . '10FFFF' - '['
|
# '{' '0020' . '10FFFF' - '['
|
||||||
|
|
||||||
|
|
||||||
# AST Plex:
|
# AST Structure:
|
||||||
#
|
#
|
||||||
# Type = 0..=5
|
# The parser outputs a JSON array of elements directly (not wrapped in an object).
|
||||||
#
|
#
|
||||||
# Text = string # just a text body
|
# Element types (currently implemented):
|
||||||
#
|
#
|
||||||
# Interpolation = string # path to variable
|
# Text = {
|
||||||
|
# "type": "text",
|
||||||
|
# "value": string # text content
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# Interpolation = {
|
||||||
|
# "type": "interpolation",
|
||||||
|
# "path": string # path to variable in data model
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# Element types (planned for MVP):
|
||||||
#
|
#
|
||||||
# Section = {
|
# Section = {
|
||||||
# v = string # item variable name for loop
|
# "type": "section",
|
||||||
# p = string # path to array for iteration
|
# "variable": string # item variable name for loop
|
||||||
# b = [Element] # section body
|
# "path": string # path to array for iteration
|
||||||
#
|
# "body": [Element] # section body (nested elements)
|
||||||
# }
|
# }
|
||||||
#
|
#
|
||||||
# End = null
|
# Element types (planned for future, not MVP):
|
||||||
|
#
|
||||||
|
# Include = {
|
||||||
|
# "type": "include",
|
||||||
|
# "path": string # path to template file to include
|
||||||
|
# }
|
||||||
#
|
#
|
||||||
# Compute = {
|
# Compute = {
|
||||||
# l = string # programing language
|
# "type": "compute",
|
||||||
# b = string # function body
|
# "language": string # programming language (dash, plpgsql, etc.)
|
||||||
|
# "body": string # function body
|
||||||
# }
|
# }
|
||||||
#
|
#
|
||||||
# Element = {
|
# AbstractSyntaxTree = [Element, ...] # array of elements
|
||||||
# t = Type # element type
|
|
||||||
# b = Text # element body
|
|
||||||
# | Interpolation
|
|
||||||
# | Section
|
|
||||||
# | End
|
|
||||||
# | Include
|
|
||||||
# | Compute
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# AbstarctSyntaxTree (ATS) = {
|
|
||||||
# e = [Element] # elements array
|
|
||||||
# }
|
|
||||||
|
|
||||||
AST=$(mktemp)
|
|
||||||
AST_key='.'
|
|
||||||
trap 'rm -f "$AST"' EXIT INT HUP
|
|
||||||
|
|
||||||
yq -o j -i '.' "$AST"
|
|
||||||
|
|
||||||
log debug "AST path: ${WHITE}${AST}"
|
|
||||||
|
|
||||||
# 0 - text
|
|
||||||
# 1 - deside tag type
|
|
||||||
# 2 - interpolation
|
|
||||||
# 3 - section
|
|
||||||
# 4 - include
|
|
||||||
# 5 - compute
|
|
||||||
STAGE=0
|
|
||||||
|
|
||||||
# is_ws(char) -> bool
|
# is_ws(char) -> bool
|
||||||
is_ws() {
|
is_ws() {
|
||||||
@@ -187,15 +173,38 @@ buf_reset() {
|
|||||||
CURRENT_STAGE_BUFFER="$STAGE_BUFFER_1"
|
CURRENT_STAGE_BUFFER="$STAGE_BUFFER_1"
|
||||||
}
|
}
|
||||||
|
|
||||||
STAGE_BUFFER_1="$(mktemp)"
|
|
||||||
CURRENT_STAGE_BUFFER=$STAGE_BUFFER_1
|
|
||||||
trap 'rm -f "$STAGE_BUFFER_1"' EXIT INT HUP
|
|
||||||
log debug "stage buffer 1: ${WHITE}$STAGE_BUFFER_1"
|
|
||||||
|
|
||||||
# json_escape(value) -> str
|
# json_escape(value) -> str
|
||||||
json_escape() {
|
json_escape() {
|
||||||
# TODO: escape functionality
|
local input="${1}"
|
||||||
printf '%s' "${1}" | sed 's/"/\\"/g'
|
local output=""
|
||||||
|
local char hex
|
||||||
|
|
||||||
|
while [ -n "$input" ]; do
|
||||||
|
char="${input%"${input#?}"}" # Get first character
|
||||||
|
input="${input#?}" # Remove first character
|
||||||
|
|
||||||
|
hex=$(printf '%d' "'$char")
|
||||||
|
|
||||||
|
case "$hex" in
|
||||||
|
34) output="${output}\\\"" ;; # "
|
||||||
|
92) output="${output}\\\\" ;; # \
|
||||||
|
10) output="${output}\\n" ;; # \n (newline)
|
||||||
|
13) output="${output}\\r" ;; # \r (carriage return)
|
||||||
|
9) output="${output}\\t" ;; # \t (tab)
|
||||||
|
8) output="${output}\\b" ;; # \b (backspace)
|
||||||
|
12) output="${output}\\f" ;; # \f (form feed)
|
||||||
|
*)
|
||||||
|
# NOTE(yukkop): escape control characters if they are not in the range 0x20-0x7E
|
||||||
|
if [ "$hex" -lt 32 ]; then
|
||||||
|
output="${output}\\u$(printf '%04x' "$hex")"
|
||||||
|
else
|
||||||
|
output="${output}${char}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
printf '%s' "$output"
|
||||||
}
|
}
|
||||||
|
|
||||||
# finds close pattern and store the char to the stage buffers separating by spaces
|
# finds close pattern and store the char to the stage buffers separating by spaces
|
||||||
@@ -489,7 +498,32 @@ parse() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
|
||||||
|
if [ -z "${AS_LIBRARY+x}" ]; then
|
||||||
|
log notice "running"
|
||||||
|
|
||||||
|
AST=$(mktemp)
|
||||||
|
AST_key='.'
|
||||||
|
trap 'rm -f "$AST"' EXIT INT HUP
|
||||||
|
|
||||||
|
yq -o j -i '.' "$AST"
|
||||||
|
|
||||||
|
log debug "AST path: ${WHITE}${AST}"
|
||||||
|
|
||||||
|
# 0 - text
|
||||||
|
# 1 - deside tag type
|
||||||
|
# 2 - interpolation
|
||||||
|
# 3 - section
|
||||||
|
# 4 - include
|
||||||
|
# 5 - compute
|
||||||
|
STAGE=0
|
||||||
|
|
||||||
|
STAGE_BUFFER_1="$(mktemp)"
|
||||||
|
CURRENT_STAGE_BUFFER=$STAGE_BUFFER_1
|
||||||
|
trap 'rm -f "$STAGE_BUFFER_1"' EXIT INT HUP
|
||||||
|
log debug "stage buffer 1: ${WHITE}$STAGE_BUFFER_1"
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
-c|--compact-output)
|
-c|--compact-output)
|
||||||
OUTPUT_ARGS="${OUTPUT_ARGS+$OUTPUT_ARGS }-I=0"
|
OUTPUT_ARGS="${OUTPUT_ARGS+$OUTPUT_ARGS }-I=0"
|
||||||
@@ -504,12 +538,12 @@ while [ $# -gt 0 ]; do
|
|||||||
exit 9
|
exit 9
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
CHAR_N=1
|
CHAR_N=1
|
||||||
LINE_N=1
|
LINE_N=1
|
||||||
|
|
||||||
while :; do
|
while :; do
|
||||||
hex="$(dd bs=1 count=1 2>/dev/null | od -An -t u1)"
|
hex="$(dd bs=1 count=1 2>/dev/null | od -An -t u1)"
|
||||||
|
|
||||||
[ -z "$hex" ] && {
|
[ -z "$hex" ] && {
|
||||||
@@ -532,12 +566,12 @@ while :; do
|
|||||||
parse "${char:?}"
|
parse "${char:?}"
|
||||||
|
|
||||||
CHAR_N=$((CHAR_N+1))
|
CHAR_N=$((CHAR_N+1))
|
||||||
done
|
done
|
||||||
|
|
||||||
log debug 'finishing'
|
log debug 'finishing'
|
||||||
|
|
||||||
# finish TEXT tag if file ends on it
|
# finish TEXT tag if file ends on it
|
||||||
if [ "$STAGE" -eq 0 ]; then
|
if [ "$STAGE" -eq 0 ]; then
|
||||||
if [ "${open_tag_flag+x}" ]; then
|
if [ "${open_tag_flag+x}" ]; then
|
||||||
unset open_tag_flag
|
unset open_tag_flag
|
||||||
printf '{' >> "$STAGE_BUFFER_1"
|
printf '{' >> "$STAGE_BUFFER_1"
|
||||||
@@ -548,8 +582,9 @@ if [ "$STAGE" -eq 0 ]; then
|
|||||||
\"type\": \"text\",
|
\"type\": \"text\",
|
||||||
\"value\": \"$(json_escape "$buf")\"
|
\"value\": \"$(json_escape "$buf")\"
|
||||||
}]" "$AST"
|
}]" "$AST"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# return the output
|
# return the output
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
yq ${OUTPUT_ARGS:-} -o j "$AST"
|
yq ${OUTPUT_ARGS:-} -o j "$AST"
|
||||||
|
fi
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
#!/bin/dash
|
|
||||||
|
|
||||||
plex_set() {
|
|
||||||
local structname key val regex base esc_key regex esc temp
|
|
||||||
structname=$1 key=$2 val=$3
|
|
||||||
|
|
||||||
# construct regex for ancestors
|
|
||||||
regex="^$key="
|
|
||||||
|
|
||||||
base=$key
|
|
||||||
while expr "$base" : '.*\.' >/dev/null; do
|
|
||||||
base=$(printf '%s\n' "$base" | sed 's/\.[^.]*$//')
|
|
||||||
esc=$(printf '%s\n' "$base" | sed 's/\./\\./g')
|
|
||||||
regex="$regex|^$esc="
|
|
||||||
done
|
|
||||||
|
|
||||||
# add descendants
|
|
||||||
esc_key="$(printf '%s\n' "$key" | sed 's/\./\\./g')"
|
|
||||||
regex="$regex|^${esc_key}\."
|
|
||||||
|
|
||||||
# remove old
|
|
||||||
# <plex>=$(printf '%s\n' "$<plex>" | grep -v -E "$regex")
|
|
||||||
temp="$(eval "printf '%s\\n' \"\$$structname\"" | grep -v -E "$regex")"
|
|
||||||
eval "$structname=\"\$temp\""
|
|
||||||
|
|
||||||
# add new
|
|
||||||
eval "$structname=\$(printf '%s\\n%s=%s\\n' \"\$$structname\" \"\$key\" \"\$val\")"
|
|
||||||
}
|
|
||||||
|
|
||||||
plex_child() {
|
|
||||||
local structname prefix
|
|
||||||
structname=$1 prefix=$2
|
|
||||||
|
|
||||||
eval printf '%s\\n' \"\$"$structname"\" \
|
|
||||||
| grep "^$prefix\." \
|
|
||||||
| sed "s|^$prefix\.||"
|
|
||||||
}
|
|
||||||
|
|
||||||
plex_val() {
|
|
||||||
local structname key
|
|
||||||
structname=$1 key=$2
|
|
||||||
eval printf '%s\n' \"\$"$structname"\" | grep "^$key=" | cut -d= -f2-
|
|
||||||
}
|
|
||||||
|
|
||||||
plex_fetch() {
|
|
||||||
local structname key
|
|
||||||
structname=$1 key=$2
|
|
||||||
if eval printf '%s\\n' \"\$"$structname"\" | grep -q "^$key="; then
|
|
||||||
eval printf '%s\\n' \"\$"$structname"\" | grep "^$key=" | cut -d= -f2-
|
|
||||||
else
|
|
||||||
eval printf '%s\\n' \"\$"$structname"\" | grep "^$key\." | sed "s|^$key\.||"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
plex_push() {
|
|
||||||
local structname prefix val max idx newidx kv
|
|
||||||
structname=${1:?}
|
|
||||||
prefix=${2:?}
|
|
||||||
val=${3:?}
|
|
||||||
|
|
||||||
# find max index
|
|
||||||
max=0
|
|
||||||
for kv in $(plex_fetch "$structname" "$prefix"); do
|
|
||||||
idx=${kv%%=*}
|
|
||||||
[ "$idx" -gt "$max" ] 2>/dev/null && max=$idx
|
|
||||||
done
|
|
||||||
|
|
||||||
newidx=$((max + 1))
|
|
||||||
plex_set "$structname" "$prefix.$newidx" "$val"
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
#!/bin/dash
|
|
||||||
|
|
||||||
PLEX_TEMP="$(mktemp -d)"
|
|
||||||
|
|
||||||
#plex_set(name, key, value)
|
|
||||||
plex_set() {
|
|
||||||
local plexfile key val regex base esc_key esc
|
|
||||||
plexfile="${PLEX_TEMP:?}${1:?}" key="${2:?}" val="${3:?}"
|
|
||||||
|
|
||||||
find PLEX_
|
|
||||||
}
|
|
||||||
|
|
||||||
plex_child() {
|
|
||||||
local plexfile key
|
|
||||||
plexfile="${PLEX_TEMP:?}${1:?}" key="${2:?}"
|
|
||||||
|
|
||||||
grep "^$key\." "" | sed "s|^$key\.||"
|
|
||||||
}
|
|
||||||
|
|
||||||
plex_val() {
|
|
||||||
local plexfile key
|
|
||||||
plexfile="${PLEX_TEMP:?}${1:?}" key="${2:?}"
|
|
||||||
grep "^$key=" | cut -d= -f2- "$plexfile"
|
|
||||||
}
|
|
||||||
|
|
||||||
plex_fetch() {
|
|
||||||
local plexfile key temp
|
|
||||||
plexfile="${PLEX_TEMP:?}${1:?}" key="${2:?}"
|
|
||||||
|
|
||||||
if temp="$(grep "^$key=" | cut -d= -f2- "$plexfile")"; then
|
|
||||||
printf '%s' "$temp"
|
|
||||||
else
|
|
||||||
grep "^$key\." "" | sed "s|^$key\.||"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
plex_push() {
|
|
||||||
local plex prefix val max idx newidx kv
|
|
||||||
plex="${1:?}" prefix="${2:?}" val="${3:?}"
|
|
||||||
|
|
||||||
# find max index
|
|
||||||
max=0
|
|
||||||
for kv in $(plex_fetch "$plex" "$prefix"); do
|
|
||||||
idx=${kv%%=*}
|
|
||||||
[ "$idx" -gt "$max" ] 2>/dev/null && max=$idx
|
|
||||||
done
|
|
||||||
|
|
||||||
newidx=$((max + 1))
|
|
||||||
plex_set "$plex" "$prefix.$newidx" "$val"
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#!/bin/dash
|
|
||||||
|
|
||||||
PLEX_TEMP="$(mktemp -d)"
|
|
||||||
trap 'rm -rf $PLEX_TEMP' EXIT
|
|
||||||
|
|
||||||
#plex_set(name, key, value)
|
|
||||||
plex_set() {
|
|
||||||
local plexfile key val
|
|
||||||
plexfile="${PLEX_TEMP:?}/${1:?}.json" key="${2:?}" val="${3:?}"
|
|
||||||
|
|
||||||
touch "$plexfile"
|
|
||||||
|
|
||||||
yq -i ".$key = \"$val\"" "$plexfile"
|
|
||||||
}
|
|
||||||
|
|
||||||
#plex_child(name, key)
|
|
||||||
plex_child() {
|
|
||||||
plex_fetch "${1:?}" "${2:?}"
|
|
||||||
}
|
|
||||||
|
|
||||||
#plex_val(name, key)
|
|
||||||
plex_val() {
|
|
||||||
plex_fetch "${1:?}" "${2:?}"
|
|
||||||
}
|
|
||||||
|
|
||||||
#plex_val(name, key)
|
|
||||||
plex_fetch() {
|
|
||||||
local plexfile key
|
|
||||||
plexfile="${PLEX_TEMP:?}/${1:?}.json" key="${2:?}"
|
|
||||||
|
|
||||||
yq -r ".$key" "$plexfile"
|
|
||||||
}
|
|
||||||
|
|
||||||
#plex_push(name, prefix, val)
|
|
||||||
plex_push() {
|
|
||||||
local plexfile prefix val
|
|
||||||
plexfile="${PLEX_TEMP:?}/${1:?}.json" prefix="${2:?}" val="${3:?}"
|
|
||||||
|
|
||||||
yq -i ".$prefix += [\"$val\"]" "$plexfile"
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#!/bin/dash
|
|
||||||
|
|
||||||
init_plex() {
|
|
||||||
local backend
|
|
||||||
backend=${1:?}
|
|
||||||
|
|
||||||
case "$backend" in
|
|
||||||
env)
|
|
||||||
. ${WORKSPACE}/src/plex/backend/env_var.sh
|
|
||||||
;;
|
|
||||||
file)
|
|
||||||
. ${WORKSPACE}/src/plex/backend/file.sh
|
|
||||||
;;
|
|
||||||
yq-go)
|
|
||||||
. ${WORKSPACE}/src/plex/backend/yq-go.sh
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/dash
|
|
||||||
|
|
||||||
# shellcheck disable=SC1091
|
|
||||||
#. "${WORKSPACE:?}/test/plex/jq_backend_time.sh"
|
|
||||||
#. "${WORKSPACE:?}/test/plex/env_backend_time.sh"
|
|
||||||
. "${WORKSPACE:?}/test/plex/jq_backend.sh"
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
#!/bin/dash
|
|
||||||
|
|
||||||
. "${WORKSPACE:?}/src/plex/plex.sh"
|
|
||||||
init_plex env
|
|
||||||
|
|
||||||
MY_STRUCT=''
|
|
||||||
|
|
||||||
math() {
|
|
||||||
awk "BEGIN {print $1}"
|
|
||||||
}
|
|
||||||
|
|
||||||
elapsed() {
|
|
||||||
local task time count decrease avg
|
|
||||||
task=$1
|
|
||||||
time=$2
|
|
||||||
count=$3
|
|
||||||
decrease=${4:-0}
|
|
||||||
avg=$(math "$time/$count-$decrease")
|
|
||||||
|
|
||||||
printf '\n[%s]\nelapsed %s seconds\n%s per second\n' \
|
|
||||||
"$task" "$avg" "$(math "1/$avg")" >&2
|
|
||||||
printf '%s' "$avg"
|
|
||||||
}
|
|
||||||
|
|
||||||
set_word_length() {
|
|
||||||
local length
|
|
||||||
length=${1:?}
|
|
||||||
|
|
||||||
# shellcheck disable=SC2183
|
|
||||||
__WORD_OFFSET_PATERN="$(printf '%*s' "$length" | tr ' ' '?')"
|
|
||||||
}
|
|
||||||
|
|
||||||
UNIQ_8_WORDS_COUNT=1000
|
|
||||||
DEFAULT_WORD_LENGTH=8
|
|
||||||
set_word_length "$DEFAULT_WORD_LENGTH"
|
|
||||||
|
|
||||||
randomword() {
|
|
||||||
local length
|
|
||||||
length=${1:-$DEFAULT_WORD_LENGTH}
|
|
||||||
LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c "$length"
|
|
||||||
}
|
|
||||||
|
|
||||||
WORDS=$(randomword $((8 * UNIQ_8_WORDS_COUNT)))
|
|
||||||
|
|
||||||
new_word() {
|
|
||||||
local prefix
|
|
||||||
prefix=${WORDS%"${WORDS#"$__WORD_OFFSET_PATERN"}"}
|
|
||||||
WORDS=${WORDS#"$__WORD_OFFSET_PATERN"}$prefix
|
|
||||||
printf '%s' "$prefix"
|
|
||||||
}
|
|
||||||
|
|
||||||
bench_set() {
|
|
||||||
local task depth count wordtime i start key d end
|
|
||||||
task=$1
|
|
||||||
depth=$2
|
|
||||||
count=$3
|
|
||||||
wordtime=$4
|
|
||||||
i=0
|
|
||||||
start=$(date +%s)
|
|
||||||
while [ "$i" -lt "$count" ]; do
|
|
||||||
key=$(new_word)
|
|
||||||
if [ "$depth" -gt 1 ]; then
|
|
||||||
d=1
|
|
||||||
while [ "$d" -lt "$depth" ]; do
|
|
||||||
key="$key.$(new_word)"
|
|
||||||
d=$((d + 1))
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
plex_set 'MY_STRUCT' "$key" "$i"
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
end=$(date +%s)
|
|
||||||
elapsed "$task" "$((end - start))" "$count" "$(math "$wordtime*$depth")" >/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFAULT_TRIES=1000
|
|
||||||
ACCURATE_TRIES=10000
|
|
||||||
SUPPER_ACCURATE_TRIES=100000
|
|
||||||
|
|
||||||
WORD_CREATE_ACCURACY="$ACCURATE_TRIES"
|
|
||||||
BENCH_ACCURACY="$DEFAULT_TRIES"
|
|
||||||
|
|
||||||
count="$WORD_CREATE_ACCURACY"
|
|
||||||
set_word_length 8
|
|
||||||
i=0
|
|
||||||
start=$(date +%s)
|
|
||||||
while [ "$i" -lt "${count:?}" ]; do
|
|
||||||
new_word >/dev/null
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
end=$(date +%s)
|
|
||||||
wordtime=$(elapsed 'Word creation' "$((end - start))" "$count")
|
|
||||||
|
|
||||||
bench_set 'Set element with depth 1 length 8' 1 "$BENCH_ACCURACY" "$wordtime"
|
|
||||||
bench_set 'Set element with depth 2 length 8' 2 "$BENCH_ACCURACY" "$wordtime"
|
|
||||||
bench_set 'Set element with depth 3 length 8' 3 "$BENCH_ACCURACY" "$wordtime"
|
|
||||||
|
|
||||||
count="$WORD_CREATE_ACCURACY"
|
|
||||||
set_word_length 2
|
|
||||||
i=0
|
|
||||||
start=$(date +%s)
|
|
||||||
while [ "$i" -lt "${count:?}" ]; do
|
|
||||||
new_word >/dev/null
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
end=$(date +%s)
|
|
||||||
wordtime=$(elapsed 'Word creation' "$((end - start))" "$count")
|
|
||||||
|
|
||||||
bench_set 'Set element with depth 1 length 2' 1 "$BENCH_ACCURACY" "$wordtime"
|
|
||||||
bench_set 'Set element with depth 2 length 2' 2 "$BENCH_ACCURACY" "$wordtime"
|
|
||||||
bench_set 'Set element with depth 3 length 2' 3 "$BENCH_ACCURACY" "$wordtime"
|
|
||||||
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
. "${WORKSPACE:?}/src/plex/plex.sh"
|
|
||||||
init_plex yq-go
|
|
||||||
|
|
||||||
plex_set ZALUPA zalupa apulaz
|
|
||||||
log error "struct:\n$WHITE$(yq . "$PLEX_TEMP/ZALUPA.json")$NC"
|
|
||||||
|
|
||||||
plex_set ZALUPA kek.zalupa apulaz
|
|
||||||
|
|
||||||
log error "struct:\n$WHITE$(yq . "$PLEX_TEMP/ZALUPA.json")$NC"
|
|
||||||
|
|
||||||
plex_set ZALUPA zalupa apulaz
|
|
||||||
|
|
||||||
log error "struct:\n$WHITE$(yq . "$PLEX_TEMP/ZALUPA.json")$NC"
|
|
||||||
|
|
||||||
plex_val ZALUPA zalupa
|
|
||||||
|
|
||||||
plex_child ZALUPA kek
|
|
||||||
|
|
||||||
plex_fetch ZALUPA kek
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
#!/bin/dash
|
|
||||||
|
|
||||||
# shellcheck disable=SC1091
|
|
||||||
. "${WORKSPACE:?}/src/plex/plex.sh"
|
|
||||||
init_plex yq-go
|
|
||||||
|
|
||||||
math() {
|
|
||||||
awk "BEGIN {print $1}"
|
|
||||||
}
|
|
||||||
|
|
||||||
elapsed() {
|
|
||||||
local task time count decrease avg
|
|
||||||
task=$1
|
|
||||||
time=$2
|
|
||||||
count=$3
|
|
||||||
decrease=${4:-0}
|
|
||||||
avg=$(math "$time/$count-$decrease")
|
|
||||||
|
|
||||||
if [ "$time" -eq 0 ]; then
|
|
||||||
log info "\n[$WHITE${task}$NC]\ninstant\n"
|
|
||||||
else
|
|
||||||
log info "\n[$WHITE${task}$NC]\nelapsed $WHITE${avg}$NC seconds\n$WHITE$(math "1/$avg")$NC per second\n"
|
|
||||||
fi
|
|
||||||
printf '%s' "$avg"
|
|
||||||
}
|
|
||||||
|
|
||||||
set_word_length() {
|
|
||||||
local length
|
|
||||||
length=${1:?}
|
|
||||||
|
|
||||||
# shellcheck disable=SC2183
|
|
||||||
__WORD_OFFSET_PATERN="$(printf '%*s' "$length" | tr ' ' '?')"
|
|
||||||
}
|
|
||||||
|
|
||||||
UNIQ_8_WORDS_COUNT=1000
|
|
||||||
DEFAULT_WORD_LENGTH=8
|
|
||||||
set_word_length "$DEFAULT_WORD_LENGTH"
|
|
||||||
|
|
||||||
randomword() {
|
|
||||||
local length
|
|
||||||
length=${1:-$DEFAULT_WORD_LENGTH}
|
|
||||||
LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c "$length"
|
|
||||||
}
|
|
||||||
|
|
||||||
WORDS=$(randomword $((8 * UNIQ_8_WORDS_COUNT)))
|
|
||||||
WORDS=0123456789abcdefg
|
|
||||||
|
|
||||||
new_word() {
|
|
||||||
local prefix
|
|
||||||
# shellcheck disable=SC2295
|
|
||||||
prefix=${WORDS%"${WORDS#${__WORD_OFFSET_PATERN:?}}"}
|
|
||||||
# shellcheck disable=SC2295
|
|
||||||
WORDS=${WORDS#${__WORD_OFFSET_PATERN:?}}$prefix
|
|
||||||
printf '%s' "$prefix"
|
|
||||||
}
|
|
||||||
|
|
||||||
bench_set() {
|
|
||||||
local task depth count wordtime i start key d end
|
|
||||||
task=$1
|
|
||||||
depth=$2
|
|
||||||
count=$3
|
|
||||||
wordtime=$4
|
|
||||||
i=0
|
|
||||||
start=$(date +%s)
|
|
||||||
while [ "$i" -lt "$count" ]; do
|
|
||||||
key=$(new_word)
|
|
||||||
if [ "$depth" -gt 1 ]; then
|
|
||||||
d=1
|
|
||||||
while [ "$d" -lt "$depth" ]; do
|
|
||||||
key="$key.$(new_word)"
|
|
||||||
d=$((d + 1))
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
set +e
|
|
||||||
plex_set 'MY_STRUCT' "$key" "$i"
|
|
||||||
error_code=$?
|
|
||||||
set -e
|
|
||||||
if [ $error_code != 0 ]; then
|
|
||||||
log error "key: $WHITE$key$NC, i: $WHITE$i$NC, struct: $WHITE$(jq . "$PLEX_TEMP/MY_STRUCT")$NC"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
end=$(date +%s)
|
|
||||||
elapsed "$task" "$((end - start))" "$count" "$(math "$wordtime*$depth")" >/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFAULT_TRIES=1000
|
|
||||||
ACCURATE_TRIES=10000
|
|
||||||
SUPPER_ACCURATE_TRIES=100000
|
|
||||||
|
|
||||||
WORD_CREATE_ACCURACY="$SUPPER_ACCURATE_TRIES"
|
|
||||||
BENCH_ACCURACY="$DEFAULT_TRIES"
|
|
||||||
|
|
||||||
count="$WORD_CREATE_ACCURACY"
|
|
||||||
set_word_length 8
|
|
||||||
i=0
|
|
||||||
start=$(date +%s)
|
|
||||||
while [ "$i" -lt "${count:?}" ]; do
|
|
||||||
new_word >/dev/null
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
end=$(date +%s)
|
|
||||||
time=$((end - start))
|
|
||||||
log debug "word creation time: $time"
|
|
||||||
wordtime=$(elapsed 'Word creation' "$time" "$count")
|
|
||||||
|
|
||||||
bench_set 'Set element with depth 1 length 8' 1 "$BENCH_ACCURACY" "$wordtime"
|
|
||||||
bench_set 'Set element with depth 2 length 8' 2 "$BENCH_ACCURACY" "$wordtime"
|
|
||||||
bench_set 'Set element with depth 3 length 8' 3 "$BENCH_ACCURACY" "$wordtime"
|
|
||||||
|
|
||||||
log notice -
|
|
||||||
|
|
||||||
count="$WORD_CREATE_ACCURACY"
|
|
||||||
set_word_length 2
|
|
||||||
i=0
|
|
||||||
start=$(date +%s)
|
|
||||||
while [ "$i" -lt "${count:?}" ]; do
|
|
||||||
new_word >/dev/null
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
end=$(date +%s)
|
|
||||||
wordtime=$(elapsed 'Word creation' "$((end - start))" "$count")
|
|
||||||
|
|
||||||
bench_set 'Set element with depth 1 length 2' 1 "$BENCH_ACCURACY" "$wordtime"
|
|
||||||
bench_set 'Set element with depth 2 length 2' 2 "$BENCH_ACCURACY" "$wordtime"
|
|
||||||
bench_set 'Set element with depth 3 length 2' 3 "$BENCH_ACCURACY" "$wordtime"
|
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
mkPgTest = testName: testDrv: pkgs.runCommand "hemar-test-${testName}"
|
mkPgTest = testName: testDrv: pkgs.runCommand "hemar-test-${testName}"
|
||||||
{
|
{
|
||||||
nativeBuildInputs = [ pkgs.coreutils pkgs.gnugrep pkgs.gnused ];
|
nativeBuildInputs = [ pkgs.coreutils pkgs.gnugrep pkgs.gnused ];
|
||||||
buildInputs = [ hemar pkgs.yq-go ];
|
buildInputs = [ hemar pkgs.yq-go pkgs.which ];
|
||||||
} ''
|
} ''
|
||||||
${builtins.readFile self.legacyPackages.${system}.helpers.posix-shell.log}
|
${builtins.readFile self.legacyPackages.${system}.helpers.posix-shell.log}
|
||||||
test=${testDrv}
|
test=${testDrv}
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ json_diff() {
|
|||||||
|
|
||||||
# run test
|
# run test
|
||||||
mkdir './test'
|
mkdir './test'
|
||||||
# shellcheck disable=SC2154
|
|
||||||
cp -r "$test"/* './test/'
|
cp -r "$test"/* './test/'
|
||||||
# shellcheck disable=SC2164
|
|
||||||
cd './test'
|
cd './test'
|
||||||
. './run.sh'
|
. './run.sh'
|
||||||
|
|||||||
126
test/package/hemar/test/function-json-escape.sh
Normal file
126
test/package/hemar/test/function-json-escape.sh
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# shellcheck disable=SC2034
|
||||||
|
AS_LIBRARY=1
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
. "$(which hemar)"
|
||||||
|
|
||||||
|
log notice "test case: ${WHITE}double quote escaping"
|
||||||
|
input='text with "quotes"'
|
||||||
|
if ! answer=$(json_escape "$input"); then
|
||||||
|
log error "test failed: ${WHITE}error during json_escape call"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
expected='text with \"quotes\"'
|
||||||
|
if [ "$answer" != "$expected" ]; then
|
||||||
|
log error "test failed: ${WHITE}wrong answer. Expected: $expected, Got: $answer"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log notice "test case: ${WHITE}backslash escaping"
|
||||||
|
input='text with \backslash'
|
||||||
|
if ! answer=$(json_escape "$input"); then
|
||||||
|
log error "test failed: ${WHITE}error during json_escape call"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
expected='text with \\backslash'
|
||||||
|
if [ "$answer" != "$expected" ]; then
|
||||||
|
log error "test failed: ${WHITE}wrong answer. Expected: $expected, Got: $answer"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log notice "test case: ${WHITE}newline escaping"
|
||||||
|
input="line1
|
||||||
|
line2"
|
||||||
|
if ! answer=$(json_escape "$input"); then
|
||||||
|
log error "test failed: ${WHITE}error during json_escape call"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
expected='line1\nline2'
|
||||||
|
if [ "$answer" != "$expected" ]; then
|
||||||
|
log error "test failed: ${WHITE}wrong answer. Expected: $expected, Got: $answer"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log notice "test case: ${WHITE}carriage return escaping"
|
||||||
|
input=$(printf 'line1\rline2')
|
||||||
|
if ! answer=$(json_escape "$input"); then
|
||||||
|
log error "test failed: ${WHITE}error during json_escape call"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
expected='line1\rline2'
|
||||||
|
if [ "$answer" != "$expected" ]; then
|
||||||
|
log error "test failed: ${WHITE}wrong answer. Expected: $expected, Got: $answer"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log notice "test case: ${WHITE}tab escaping"
|
||||||
|
input="text with tabs"
|
||||||
|
if ! answer=$(json_escape "$input"); then
|
||||||
|
log error "test failed: ${WHITE}error during json_escape call"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
expected='text\twith\ttabs'
|
||||||
|
if [ "$answer" != "$expected" ]; then
|
||||||
|
log error "test failed: ${WHITE}wrong answer. Expected: $expected, Got: $answer"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log notice "test case: ${WHITE}control character escaping"
|
||||||
|
# NOTE: Test with a control character (bell, 0x07)
|
||||||
|
input=$(printf 'text\007with\007control')
|
||||||
|
if ! answer=$(json_escape "$input"); then
|
||||||
|
log error "test failed: ${WHITE}error during json_escape call"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# NOTE: Control character should be escaped as \u0007
|
||||||
|
expected='text\u0007with\u0007control'
|
||||||
|
if [ "$answer" != "$expected" ]; then
|
||||||
|
log error "test failed: ${WHITE}wrong answer. Expected: $expected, Got: $answer"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log notice "test case: ${WHITE}complex string with multiple special chars"
|
||||||
|
input='text with "quotes" and \backslashes
|
||||||
|
and newlines and tabs'
|
||||||
|
if ! answer=$(json_escape "$input"); then
|
||||||
|
log error "test failed: ${WHITE}error during json_escape call"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
expected='text with \"quotes\" and \\backslashes\nand newlines\tand tabs'
|
||||||
|
if [ "$answer" != "$expected" ]; then
|
||||||
|
log error "test failed: ${WHITE}wrong answer. Expected: $expected, Got: $answer"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log notice "test case: ${WHITE}empty string"
|
||||||
|
input=''
|
||||||
|
if ! answer=$(json_escape "$input"); then
|
||||||
|
log error "test failed: ${WHITE}error during json_escape call"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$answer" ]; then
|
||||||
|
log error "test failed: ${WHITE}empty string should produce empty output"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log notice "test case: ${WHITE}plain text (no escaping needed)"
|
||||||
|
input='plain text without special chars'
|
||||||
|
if ! answer=$(json_escape "$input"); then
|
||||||
|
log error "test failed: ${WHITE}error during json_escape call"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$answer" != "$input" ]; then
|
||||||
|
log error "test failed: ${WHITE}plain text should remain unchanged. Expected: $input, Got: $answer"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log notice "test passed"
|
||||||
|
|
||||||
Reference in New Issue
Block a user