From 10f2db01776937d715289e45097eaca7b7e2ef0b Mon Sep 17 00:00:00 2001 From: Thomas Perschak Date: Thu, 6 Nov 2025 00:06:52 +0100 Subject: [PATCH 1/3] parse_args fails to load during tests, simplify as it seems it's anyhow overkill for this --- tests/helpers.tcl | 21 ++++++++------------- tests/number.test | 4 ++-- tests/pretty.test | 3 +-- tests/set.test | 3 +-- tests/unset.test | 3 +-- 5 files changed, 13 insertions(+), 21 deletions(-) diff --git a/tests/helpers.tcl b/tests/helpers.tcl index 13ae0cb..d5ce82e 100644 --- a/tests/helpers.tcl +++ b/tests/helpers.tcl @@ -154,19 +154,14 @@ proc _compare_json {opts j1 j2 {path {}}} { #<<< #>>> proc compare_json args { #<<< - parse_args $args { - -subset {-default none} - j1 {} - j2 {} - } opts - - try { - _compare_json $opts [dict get $opts j1] [dict get $opts j2] - } trap {RL TEST JSON_MISMATCH} {errmsg options} { - return $errmsg - } on ok {} { - return match - } + set opts [list -subset none j1 [lindex $args 0] j2 [lindex $args 1]] + try { + _compare_json $opts [dict get $opts j1] [dict get $opts j2] + } trap {RL TEST JSON_MISMATCH} {errmsg options} { + return $errmsg + } on ok {} { + return match + } } #>>> diff --git a/tests/number.test b/tests/number.test index 5d5eda8..bf4ac1c 100644 --- a/tests/number.test +++ b/tests/number.test @@ -239,14 +239,14 @@ test number-2.3 {json number, not a number} -body { #<<< list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o -} -result [list 1 {can't use non-numeric string "foo" as operand of "+"} {ARITH DOMAIN {non-numeric string}}] +} -result [list 1 {can't use non-numeric string as operand of "+"} {ARITH DOMAIN {non-numeric string}}] #>>> test number-2.4 {json number, not a number: empty string} -body { #<<< set code [catch {json number ""} r o] list $code $r [dict get $o -errorcode] } -cleanup { unset -nocomplain code r o -} -result [list 1 {can't use empty string "" as operand of "+"} {ARITH DOMAIN {empty string}}] +} -result [list 1 {can't use empty string as operand of "+"} {ARITH DOMAIN {empty string}}] #>>> ::tcltest::cleanupTests diff --git a/tests/pretty.test b/tests/pretty.test index 1bd6e51..4215b50 100644 --- a/tests/pretty.test +++ b/tests/pretty.test @@ -4,8 +4,7 @@ if {"::tcltest" ni [namespace children]} { } package require rl_json -package require parse_args -namespace path {::rl_json ::parse_args} +namespace path {::rl_json} test pretty-1.1 {Basic pretty-print} -body { #<<< json pretty {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null,{"inner": "obj"}]}} diff --git a/tests/set.test b/tests/set.test index ce5d737..8da79bb 100644 --- a/tests/set.test +++ b/tests/set.test @@ -4,8 +4,7 @@ if {"::tcltest" ni [namespace children]} { } package require rl_json -package require parse_args -namespace path {::rl_json ::parse_args} +namespace path {::rl_json} source [file join [file dirname [info script]] helpers.tcl] diff --git a/tests/unset.test b/tests/unset.test index 1f0dc34..beb76d9 100644 --- a/tests/unset.test +++ b/tests/unset.test @@ -4,8 +4,7 @@ if {"::tcltest" ni [namespace children]} { } package require rl_json -package require parse_args -namespace path {::rl_json ::parse_args} +namespace path {::rl_json} source [file join [file dirname [info script]] helpers.tcl] From 23fc406a27affc33e4b4909e360c55edbd0fe024 Mon Sep 17 00:00:00 2001 From: Thomas Perschak Date: Thu, 6 Nov 2025 10:33:01 +0100 Subject: [PATCH 2/3] enhancements for the pretty command: -compact -indent -arrays --- doc/json.n | 61 +++++++++-- generic/api.c | 43 +++++++- generic/rl_json.c | 130 ++++++++++++++++++++--- generic/rl_json.decls | 3 + generic/rl_jsonDecls.h | 9 +- generic/rl_jsonInt.h | 5 + generic/rl_jsonStubInit.c | 1 + tests/pretty.test | 210 +++++++++++++++++++++++++++++++++++++- 8 files changed, 435 insertions(+), 27 deletions(-) diff --git a/doc/json.n b/doc/json.n index 55567c9..a44572e 100644 --- a/doc/json.n +++ b/doc/json.n @@ -4,15 +4,15 @@ '\" See the file "LICENSE" for information on usage and redistribution '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. '\" -.TH json n 0.14.0 rl_json "RubyLane/JSON Package Commands" +.TH json n 0.15.0 rl_json "RubyLane/JSON Package Commands" .so man.macros .BS '\" Note: do not modify the .SH NAME line immediately below! .SH NAME -json \- Parse, manipulate and produce JSON documents +json \- Parse, manipulate and produce JSON documents .SH SYNOPSIS .nf -\fBpackage require rl_json\fR ?\fB0.14.0\fR? +\fBpackage require rl_json\fR ?\fB0.15.0\fR? \fBjson get\fR ?\fB-default\fR \fIdefaultValue\fR? \fIjsonValue\fR ?\fIkey ...\fR? \fBjson extract\fR ?\fB-default\fR \fIdefaultValue\fR? \fIjsonValue\fR ?\fIkey ...\fR? @@ -28,9 +28,10 @@ json \- Parse, manipulate and produce JSON documents \fBjson boolean\fR \fIvalue\fR \fBjson object\fR \fI?key value ?key value ...??\fR \fBjson array\fR \fIelem ...\fR +\fBjson autoarray\fR \fIvalue ...\fR \fBjson bool\fR \fIvalue\fR \fBjson normalize\fR \fIjsonValue\fR -\fBjson pretty\fR ?\fB-intent\fR \fIindent\fR? \fIjsonValue\fR ?\fIkey ...\fR? +\fBjson pretty\fR ?\fB-indent\fR \fIindent\fR? ?\fB-compact\fR? ?\fB-arrays\fR \fImode\fR? \fIjsonValue\fR ?\fIkey ...\fR? \fBjson template\fR \fIjsonValue\fR ?\fIdictionary\fR? \fBjson isnull\fR \fIjsonValue\fR ?\fIkey ...\fR? \fBjson type\fR \fIjsonValue\fR ?\fIkey ...\fR? @@ -170,6 +171,26 @@ Return a JSON array containing each of the elements given. \fIelem\fR is a list of two elements, the first being the type {string, number, boolean, null, object, array, json}, and the second being the value. .TP +\fBjson autoarray \fI?value ...?\fR +. +Return a JSON array containing each of the values given, with automatic type detection. +Unlike \fBjson array\fR which requires explicit type specification, \fBjson autoarray\fR +automatically determines the appropriate JSON type for each value: +.RS +.IP \(bu 3 +Values exactly matching "true" or "false" (case-sensitive) are converted to JSON booleans. +.IP \(bu 3 +Values that can be parsed as valid JSON numbers are converted to JSON numbers. +.IP \(bu 3 +All other values are converted to JSON strings. +.RE +.PP +For example: +.CS + json autoarray 1 2.5 true false "hello world" 42 + # Returns: [1,2.5,true,false,"hello world",42] +.CE +.TP \fBjson foreach \fIvarList1 jsonValue1\fR ?\fIvarList2 jsonValue2 ...\fR? \fIscript\fR . Evaluate \fIscript\fR in a loop in a similar way to the \fBforeach\fR command. @@ -232,12 +253,38 @@ Return a version of the input \fIjsonValue\fR, i.e., with all optional whitespace trimmed. .TP -\fBjson pretty\fR ?\fB-indent\fR \fIindent\fR? \fIjsonValue\fR ?\fIkey ...\fR? +\fBjson pretty\fR ?\fB-indent\fR \fIindent\fR? ?\fB-compact\fR? ?\fB-arrays\fR \fImode\fR? \fIjsonValue\fR ?\fIkey ...\fR? . Returns a pretty-printed string representation of \fIjsonValue\fR, found by following the path of \fIkey\fRs. Useful for debugging or inspecting the -structure of JSON data. If \fB-indent\fR is supplied, use \fIindent\fR for -each level of indent, otherwise default to four spaces. +structure of JSON data. +.RS +.PP +The following options control the formatting: +.TP +\fB-indent\fR \fIindent\fR +. +Use \fIindent\fR for each level of indent. Defaults to four spaces if not specified. +.TP +\fB-compact\fR +. +Return a compact, single-line representation with no extra whitespace. This is equivalent +to \fBjson normalize\fR but provided for convenience when using other pretty options. +When this option is used, \fB-indent\fR and \fB-arrays\fR are ignored. +.TP +\fB-arrays\fR \fImode\fR +. +Control how arrays are formatted. \fImode\fR must be one of: +.RS +.IP \fBinline\fR 10 +All arrays are formatted on a single line: [1,2,3] +.IP \fBmultiline\fR 10 +All arrays are formatted with one element per line. +.RE +.PP +If not specified, arrays with 3 or fewer elements are formatted inline, while larger +arrays are formatted with one element per line. +.RE .TP \fBjson decode \fIbytes\fR ?\fIencoding\fR? . diff --git a/generic/api.c b/generic/api.c index 5319e21..eca6cba 100644 --- a/generic/api.c +++ b/generic/api.c @@ -31,10 +31,10 @@ int JSON_NewJNumberObj(Tcl_Interp* interp, Tcl_Obj* number, Tcl_Obj** new) //{{{ int JSON_NewJBooleanObj(Tcl_Interp* interp, Tcl_Obj* boolean, Tcl_Obj** new) //{{{ { struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); - int bool; + int boolVal; - TEST_OK(Tcl_GetBooleanFromObj(interp, boolean, &bool)); - replace_tclobj(new, bool ? l->json_true : l->json_false); + TEST_OK(Tcl_GetBooleanFromObj(interp, boolean, &boolVal)); + replace_tclobj(new, boolVal ? l->json_true : l->json_false); return TCL_OK; } @@ -952,7 +952,42 @@ int JSON_Pretty(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, Tcl_Obj** pre replace_tclobj(&pad, l->tcl_empty); Tcl_DStringInit(&ds); - retval = json_pretty(interp, obj, indent, pad, &ds); + retval = json_pretty_ex(interp, obj, indent, pad, -1, &ds); + + if (retval == TCL_OK) + replace_tclobj(prettyString, Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds))); + + Tcl_DStringFree(&ds); + release_tclobj(&pad); + release_tclobj(&lindent); + + return retval; +} + +//}}} +int JSON_Pretty_Ex(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, int compact, int arrays_inline, Tcl_Obj** prettyString) //{{{ +{ + int retval = TCL_OK; + Tcl_DString ds; + Tcl_Obj* lindent = NULL; + Tcl_Obj* pad = NULL; + struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); + + // Handle compact mode - just normalize (remove all whitespace) + if (compact) { + retval = JSON_Normalize(interp, obj, prettyString); + return retval; + } + + // Normal pretty printing with formatting options + if (indent == NULL) { + replace_tclobj(&lindent, get_string(l, " ", 4)); + indent = lindent; + } + + replace_tclobj(&pad, l->tcl_empty); + Tcl_DStringInit(&ds); + retval = json_pretty_ex(interp, obj, indent, pad, arrays_inline, &ds); if (retval == TCL_OK) replace_tclobj(prettyString, Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds))); diff --git a/generic/rl_json.c b/generic/rl_json.c index 34aec51..b644dfd 100644 --- a/generic/rl_json.c +++ b/generic/rl_json.c @@ -121,6 +121,7 @@ static const char *extension_str[] = { static int new_json_value_from_list(Tcl_Interp* interp, int objc, Tcl_Obj *const objv[], Tcl_Obj** res); static int NRforeach_next_loop_bottom(ClientData cdata[], Tcl_Interp* interp, int retcode); static int json_pretty_dbg(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, Tcl_DString* ds); +int json_pretty_ex(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, int arrays_inline, Tcl_DString* ds); static int _setdir(Tcl_Interp* interp) //{{{ { @@ -1339,7 +1340,7 @@ static int foreach(Tcl_Interp* interp, int objc, Tcl_Obj *const objv[], enum col } //}}} -int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, Tcl_DString* ds) //{{{ +int json_pretty_ex(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, int arrays_inline, Tcl_DString* ds) //{{{ { int pad_len, next_pad_len, count; enum json_types type; @@ -1401,11 +1402,11 @@ int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad append_json_string(&scx, k); Tcl_DStringAppend(ds, ": ", 2); - Tcl_GetStringFromObj(k, &k_len); + Tcl_GetStringFromObj(k, &k_len); if (k_len < max) Tcl_DStringAppend(ds, key_pad_buf, max-k_len); - - if (json_pretty(interp, v, indent, next_pad, ds) != TCL_OK) { + + if (json_pretty_ex(interp, v, indent, next_pad, arrays_inline, ds) != TCL_OK) { Tcl_DictObjDone(&search); retval = TCL_ERROR; goto finally; @@ -1429,6 +1430,7 @@ int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad { int i, oc; Tcl_Obj** ov; + int force_inline, force_multiline, should_inline; TEST_OK_LABEL(finally, retval, Tcl_ListObjGetElements(interp, val, &oc, &ov)); @@ -1436,20 +1438,38 @@ int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad Tcl_AppendObjToObj(next_pad, indent); next_pad_str = Tcl_GetStringFromObj(next_pad, &next_pad_len); + // Determine array formatting: inline vs multiline + force_inline = (arrays_inline == 1); + force_multiline = (arrays_inline == 0); + // Auto heuristic: small arrays (<=3 elements) inline by default + should_inline = (!force_multiline) && (force_inline || oc <= 3); + if (oc == 0) { Tcl_DStringAppend(ds, "[]", 2); + } else if (should_inline) { + // Inline format: [1,2,3] + Tcl_DStringAppend(ds, "[", 1); + count = 0; + for (i=0; ijson_true)); + } else if (len == 5 && strcmp(str, "false") == 0) { + replace_tclobj(&elem, JSON_NewJvalObj(JSON_BOOL, l->json_false)); + } else { + // Try to parse as a number + int is_number = (force_json_number(interp, l, objv[i], &forced) == TCL_OK); + + if (is_number) { + // It's a valid JSON number + replace_tclobj(&elem, JSON_NewJvalObj(JSON_NUMBER, forced)); + release_tclobj(&forced); + } else { + // Default to string + // Clear any error message from failed number conversion + Tcl_ResetResult(interp); + replace_tclobj(&elem, JSON_NewJvalObj(JSON_STRING, objv[i])); + } + } + + TEST_OK_LABEL(finally, retval, Tcl_ListObjAppendElement(interp, val, elem)); + } + Tcl_SetObjResult(interp, JSON_NewJvalObj(JSON_ARRAY, val)); + +finally: + release_tclobj(&elem); + release_tclobj(&val); + release_tclobj(&forced); + return retval; +} + //}}} static int jsonDecode(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { @@ -3181,18 +3249,33 @@ static int jsonPretty(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *c Tcl_Obj* indent = NULL; Tcl_Obj* target = NULL; int argbase = 1; + int compact = 0; + int arrays_inline = -1; // -1 = default/auto, 0 = multiline, 1 = inline static const char* opts[] = { "-indent", + "-compact", + "-arrays", "--", // Unnecessary for this case, but supported for convention NULL }; enum { OPT_INDENT, + OPT_COMPACT, + OPT_ARRAYS, OPT_END_OPTIONS }; + static const char* array_modes[] = { + "inline", + "multiline", + NULL + }; + enum { + ARRAYS_INLINE, + ARRAYS_MULTILINE + }; enum {A_cmd, A_VAL, A_args}; - CHECK_MIN_ARGS_LABEL(finally, code, "pretty ?-indent indent? json_val ?key ...?"); + CHECK_MIN_ARGS_LABEL(finally, code, "pretty ?-indent indent? ?-compact? ?-arrays inline|multiline? json_val ?key ...?"); // Consume any leading options {{{ while (argbase < objc) { @@ -3214,18 +3297,35 @@ static int jsonPretty(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *c argbase += 2; break; + case OPT_COMPACT: + compact = 1; + argbase++; + break; + + case OPT_ARRAYS: { + int array_mode; + if (objc - argbase < 2) { + Tcl_SetErrorCode(interp, "TCL", "ARGUMENT", "MISSING", NULL); + THROW_ERROR_LABEL(finally, code, "missing argument to \"-arrays\""); + } + TEST_OK_LABEL(finally, code, Tcl_GetIndexFromObj(interp, objv[argbase+1], array_modes, "array mode", TCL_EXACT, &array_mode)); + arrays_inline = (array_mode == ARRAYS_INLINE) ? 1 : 0; + argbase += 2; + break; + } + case OPT_END_OPTIONS: argbase++; goto endoptions; default: - THROW_ERROR_LABEL(finally, code, "Unhandled get option idx"); + THROW_ERROR_LABEL(finally, code, "Unhandled pretty option idx"); } } endoptions: if (objc == argbase) { - Tcl_WrongNumArgs(interp, 1, objv, "?-default defaultValue? json_val ?key ...?"); + Tcl_WrongNumArgs(interp, 1, objv, "?-indent indent? ?-compact? ?-arrays inline|multiline? json_val ?key ...?"); code = TCL_ERROR; goto finally; } @@ -3237,7 +3337,7 @@ static int jsonPretty(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *c replace_tclobj(&target, objv[argbase]); } - TEST_OK_LABEL(finally, code, JSON_Pretty(interp, target, indent, &pretty)); + TEST_OK_LABEL(finally, code, JSON_Pretty_Ex(interp, target, indent, compact, arrays_inline, &pretty)); Tcl_SetObjResult(interp, pretty); @@ -3602,6 +3702,7 @@ static int jsonNRObj(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *co "boolean", "object", "array", + "autoarray", "decode", @@ -3643,6 +3744,7 @@ static int jsonNRObj(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *co M_BOOLEAN, M_OBJECT, M_ARRAY, + M_AUTOARRAY, M_DECODE, // Debugging M_FREE_CACHE, @@ -3678,6 +3780,7 @@ static int jsonNRObj(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *co case M_BOOLEAN: return jsonBoolean(cdata, interp, objc-1, objv+1); case M_OBJECT: return jsonObject(cdata, interp, objc-1, objv+1); case M_ARRAY: return jsonArray(cdata, interp, objc-1, objv+1); + case M_AUTOARRAY: return jsonAutoArray(cdata, interp, objc-1, objv+1); case M_DECODE: return jsonDecode(cdata, interp, objc-1, objv+1); case M_ISNULL: return jsonIsNull(cdata, interp, objc-1, objv+1); case M_TEMPLATE: return jsonTemplate(cdata, interp, objc-1, objv+1); @@ -4128,6 +4231,7 @@ DLLEXPORT int Rl_json_Init(Tcl_Interp* interp) //{{{ Tcl_CreateObjCommand(interp, ENS "boolean", jsonBoolean, l, NULL); Tcl_CreateObjCommand(interp, ENS "object", jsonObject, l, NULL); Tcl_CreateObjCommand(interp, ENS "array", jsonArray, l, NULL); + Tcl_CreateObjCommand(interp, ENS "autoarray", jsonAutoArray, l, NULL); Tcl_CreateObjCommand(interp, ENS "decode", jsonDecode, l, NULL); Tcl_CreateObjCommand(interp, ENS "isnull", jsonIsNull, l, NULL); Tcl_CreateObjCommand(interp, ENS "template", jsonTemplate, l, NULL); diff --git a/generic/rl_json.decls b/generic/rl_json.decls index 9354f00..4ff085c 100644 --- a/generic/rl_json.decls +++ b/generic/rl_json.decls @@ -115,3 +115,6 @@ declare 32 generic { declare 33 generic { int JSON_Valid(Tcl_Interp* interp, Tcl_Obj* json, int* valid, enum extensions extensions, struct parse_error* details) } +declare 34 generic { + int JSON_Pretty_Ex(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, int compact, int arrays_inline, Tcl_Obj** prettyString) +} diff --git a/generic/rl_jsonDecls.h b/generic/rl_jsonDecls.h index 1ec3823..421abc9 100644 --- a/generic/rl_jsonDecls.h +++ b/generic/rl_jsonDecls.h @@ -117,8 +117,12 @@ EXTERN int JSON_Foreach(Tcl_Interp*interp, Tcl_Obj*iterators, EXTERN int JSON_Valid(Tcl_Interp*interp, Tcl_Obj*json, int*valid, enum extensions extensions, struct parse_error*details); +/* 34 */ +EXTERN int JSON_Pretty_Ex(Tcl_Interp*interp, Tcl_Obj*obj, + Tcl_Obj*indent, int compact, + int arrays_inline, Tcl_Obj**prettyString); -typedef struct Rl_jsonStubs { +typedef struct TcljsonStubs { int magic; void *hooks; @@ -156,6 +160,7 @@ typedef struct Rl_jsonStubs { int (*jSON_Decode) (Tcl_Interp*interp, Tcl_Obj*bytes, Tcl_Obj*encoding, Tcl_Obj**decodedstring); /* 31 */ int (*jSON_Foreach) (Tcl_Interp*interp, Tcl_Obj*iterators, JSON_ForeachBody*body, enum collecting_mode collect, Tcl_Obj**res, ClientData cdata); /* 32 */ int (*jSON_Valid) (Tcl_Interp*interp, Tcl_Obj*json, int*valid, enum extensions extensions, struct parse_error*details); /* 33 */ + int (*jSON_Pretty_Ex) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*indent, int compact, int arrays_inline, Tcl_Obj**prettyString); /* 34 */ } Rl_jsonStubs; extern const Rl_jsonStubs *rl_jsonStubsPtr; @@ -238,6 +243,8 @@ extern const Rl_jsonStubs *rl_jsonStubsPtr; (rl_jsonStubsPtr->jSON_Foreach) /* 32 */ #define JSON_Valid \ (rl_jsonStubsPtr->jSON_Valid) /* 33 */ +#define JSON_Pretty_Ex \ + (rl_jsonStubsPtr->jSON_Pretty_Ex) /* 34 */ #endif /* defined(USE_RL_JSON_STUBS) */ diff --git a/generic/rl_jsonInt.h b/generic/rl_jsonInt.h index 56867a5..beef560 100644 --- a/generic/rl_jsonInt.h +++ b/generic/rl_jsonInt.h @@ -1,6 +1,10 @@ #ifndef _RL_JSONINT #define _RL_JSONINT +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "rl_json.h" #include "tclstuff.h" #include @@ -249,6 +253,7 @@ int build_template_actions(Tcl_Interp* interp, Tcl_Obj* template, Tcl_Obj** acti int convert_to_tcl(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj** out); int resolve_path(Tcl_Interp* interp, Tcl_Obj* src, Tcl_Obj *const pathv[], int pathc, Tcl_Obj** target, const int exists, const int modifiers, Tcl_Obj* def); int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, Tcl_DString* ds); +int json_pretty_ex(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, int arrays_inline, Tcl_DString* ds); void foreach_state_free(struct foreach_state* state); #define TEMPLATE_TYPE(s, len, out) \ diff --git a/generic/rl_jsonStubInit.c b/generic/rl_jsonStubInit.c index 4b65f93..279fd55 100644 --- a/generic/rl_jsonStubInit.c +++ b/generic/rl_jsonStubInit.c @@ -39,6 +39,7 @@ const Rl_jsonStubs rl_jsonStubs = { JSON_Decode, /* 31 */ JSON_Foreach, /* 32 */ JSON_Valid, /* 33 */ + JSON_Pretty_Ex, /* 34 */ }; /* !END!: Do not edit above this line. */ diff --git a/tests/pretty.test b/tests/pretty.test index 4215b50..c1407bf 100644 --- a/tests/pretty.test +++ b/tests/pretty.test @@ -50,7 +50,7 @@ test pretty-1.2 {Basic pretty-print, different indent} -body { #<<< #>>> test pretty-2.1 {too few args} -body { #<<< json pretty -} -returnCodes error -result {wrong # args: should be "pretty pretty ?-indent indent? json_val ?key ...?"} +} -returnCodes error -result {wrong # args: should be "pretty pretty ?-indent indent? ?-compact? ?-arrays inline|multiline? json_val ?key ...?"} #>>> test pretty-3.1 {path} -body { #<<< json pretty {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null,{"inner": "obj"}]}} {This is a much longer key} @@ -92,10 +92,216 @@ test debug-1.1 {Basic debug pretty-print} -body { #<<< }$} #>>> +test pretty-4.1 {compact output} -body { + json pretty -compact {{"foo":"bar","array":[1,2,3]}} +} -result {{"foo":"bar","array":[1,2,3]}} + +test pretty-5.1 {arrays inline mode} -body { + json pretty -arrays inline {{"arr":[1,2,3,4,5]}} +} -result {{ + "arr": [1,2,3,4,5] +}} + +test pretty-5.2 {arrays multiline mode} -body { + json pretty -arrays multiline {{"arr":[1,2,3]}} +} -result {{ + "arr": [ + 1, + 2, + 3 + ] +}} + +test pretty-5.3 {arrays default auto mode - small inline} -body { + json pretty {{"small":[1,2,3]}} +} -result {{ + "small": [1,2,3] +}} + +test pretty-5.4 {arrays default auto mode - large multiline} -body { + json pretty {{"large":[1,2,3,4,5]}} +} -result {{ + "large": [ + 1, + 2, + 3, + 4, + 5 + ] +}} + +test pretty-6.1 {combine -indent with -arrays inline} -body { + json pretty -indent " " -arrays inline {{"arr":[1,2,3,4,5]}} +} -result {{ + "arr": [1,2,3,4,5] +}} + +test pretty-6.2 {combine -indent with -arrays multiline} -body { + json pretty -indent " " -arrays multiline {{"arr":[1,2,3]}} +} -result {{ + "arr": [ + 1, + 2, + 3 + ] +}} + +test pretty-6.3 {tab indent with arrays inline} -body { + json pretty -indent "\t" -arrays inline {{"data":[10,20,30]}} +} -result {{ + "data": [10,20,30] +}} + +test pretty-6.4 {tab indent with arrays multiline} -body { + json pretty -indent "\t" -arrays multiline {{"data":[10,20]}} +} -result {{ + "data": [ + 10, + 20 + ] +}} + +test pretty-7.1 {compact overrides indent} -body { + json pretty -compact -indent " " {{"foo":"bar"}} +} -result {{"foo":"bar"}} + +test pretty-7.2 {compact overrides arrays inline} -body { + json pretty -compact -arrays inline {{"arr":[1,2,3]}} +} -result {{"arr":[1,2,3]}} + +test pretty-7.3 {compact overrides arrays multiline} -body { + json pretty -compact -arrays multiline {{"arr":[1,2,3,4,5]}} +} -result {{"arr":[1,2,3,4,5]}} + +test pretty-7.4 {compact with all options} -body { + json pretty -compact -indent "\t" -arrays multiline {{"x":[1,2],"y":{"z":"w"}}} +} -result {{"x":[1,2],"y":{"z":"w"}}} + +test pretty-8.1 {nested objects with custom indent} -body { + json pretty -indent " " {{"outer":{"inner":{"deep":"value"}}}} +} -result {{ + "outer": { + "inner": { + "deep": "value" + } + } +}} + +test pretty-8.2 {nested arrays with multiline} -body { + json pretty -arrays multiline {{"matrix":[[1,2],[3,4]]}} +} -result {{ + "matrix": [ + [ + 1, + 2 + ], + [ + 3, + 4 + ] + ] +}} + +test pretty-8.3 {nested arrays with inline} -body { + json pretty -arrays inline {{"matrix":[[1,2],[3,4]]}} +} -result {{ + "matrix": [[1,2],[3,4]] +}} + +test pretty-8.4 {mixed nested structures} -body { + json pretty -indent " " {{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}} +} -result {{ + "users": [{ + "name": "Alice", + "age": 30 + },{ + "name": "Bob", + "age": 25 + }] +}} + +test pretty-9.1 {empty array with default} -body { + json pretty {{"empty":[]}} +} -result {{ + "empty": [] +}} + +test pretty-9.2 {empty array with arrays inline} -body { + json pretty -arrays inline {{"empty":[]}} +} -result {{ + "empty": [] +}} + +test pretty-9.3 {empty array with arrays multiline} -body { + json pretty -arrays multiline {{"empty":[]}} +} -result {{ + "empty": [] +}} + +test pretty-9.4 {empty object} -body { + json pretty {{"empty":{}}} +} -result {{ + "empty": {} +}} + +test pretty-10.1 {complex structure default formatting} -body { + json pretty {{"api":{"version":"1.0","endpoints":["/user","/data","/login","/logout"]}}} +} -result {{ + "api": { + "version": "1.0", + "endpoints": [ + "/user", + "/data", + "/login", + "/logout" + ] + } +}} + +test pretty-10.2 {complex structure with indent and inline arrays} -body { + json pretty -indent "\t" -arrays inline {{"config":{"debug":true,"ports":[8080,8081,8082],"name":"server"}}} +} -result {{ + "config": { + "debug": true, + "ports": [8080,8081,8082], + "name": "server" + } +}} + +test pretty-10.3 {multiple data types} -body { + json pretty {{"string":"text","number":42,"bool":true,"null":null,"array":[1,2]}} +} -result {{ + "string": "text", + "number": 42, + "bool": true, + "null": null, + "array": [1,2] +}} + +test pretty-11.1 {single space indent} -body { + json pretty -indent " " {{"a":"b"}} +} -result {{ + "a": "b" +}} + +test pretty-11.2 {large indent} -body { + json pretty -indent " " {{"x":"y"}} +} -result {{ + "x": "y" +}} + +test pretty-11.3 {three space indent with nested} -body { + json pretty -indent " " {{"outer":{"inner":"val"}}} +} -result {{ + "outer": { + "inner": "val" + } +}} + # Coverage golf test pretty-jsonPretty-1.1 {} -body {json pretty -indent} -returnCodes error -result {missing argument to "-indent"} -errorCode {TCL ARGUMENT MISSING} test pretty-jsonPretty-2.1 {} -body {json pretty -- 1} -result 1 -test pretty-jsonPretty-3.1 {} -body {json pretty -indent { }} -returnCodes error -result {wrong # args: should be "pretty ?-default defaultValue? json_val ?key ...?"} -errorCode {TCL WRONGARGS} +test pretty-jsonPretty-3.1 {} -body {json pretty -indent { }} -returnCodes error -result {wrong # args: should be "pretty ?-indent indent? ?-compact? ?-arrays inline|multiline? json_val ?key ...?"} -errorCode {TCL WRONGARGS} test pretty-jsonPretty-4.1 {} -body {json pretty bad} -returnCodes error -result {Error parsing JSON value: Illegal character at offset 0} -errorCode {RL JSON PARSE {Illegal character} bad 0} test pretty-jsonPretty-5.1 {} -body {json pretty -bad} -returnCodes error -result {bad option "-bad": must be *} -errorCode {TCL LOOKUP INDEX option -bad} -match glob From c3588aa93f36f90cf33808f7ab915f27a41b09d0 Mon Sep 17 00:00:00 2001 From: Thomas Perschak Date: Thu, 6 Nov 2025 11:52:32 +0100 Subject: [PATCH 3/3] pretty command now accepts the -nopadding switch cleanup the pretty.test --- generic/api.c | 32 +--------- generic/rl_json.c | 54 ++++++++++------ generic/rl_json.decls | 5 +- generic/rl_jsonDecls.h | 13 ++-- generic/rl_jsonInt.h | 3 +- generic/rl_jsonStubInit.c | 3 +- tests/pretty.test | 131 +++++++++++++++++++------------------- 7 files changed, 109 insertions(+), 132 deletions(-) diff --git a/generic/api.c b/generic/api.c index eca6cba..ed97463 100644 --- a/generic/api.c +++ b/generic/api.c @@ -937,35 +937,7 @@ int JSON_Normalize(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj** normalized) //{{{ } //}}} -int JSON_Pretty(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, Tcl_Obj** prettyString) //{{{ -{ - int retval = TCL_OK; - Tcl_DString ds; - Tcl_Obj* lindent = NULL; - Tcl_Obj* pad = NULL; - struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); - - if (indent == NULL) { - replace_tclobj(&lindent, get_string(l, " ", 4)); - indent = lindent; - } - - replace_tclobj(&pad, l->tcl_empty); - Tcl_DStringInit(&ds); - retval = json_pretty_ex(interp, obj, indent, pad, -1, &ds); - - if (retval == TCL_OK) - replace_tclobj(prettyString, Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds))); - - Tcl_DStringFree(&ds); - release_tclobj(&pad); - release_tclobj(&lindent); - - return retval; -} - -//}}} -int JSON_Pretty_Ex(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, int compact, int arrays_inline, Tcl_Obj** prettyString) //{{{ +int JSON_Pretty(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, int nopadding, int compact, int arrays_inline, Tcl_Obj** prettyString) //{{{ { int retval = TCL_OK; Tcl_DString ds; @@ -987,7 +959,7 @@ int JSON_Pretty_Ex(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, int compac replace_tclobj(&pad, l->tcl_empty); Tcl_DStringInit(&ds); - retval = json_pretty_ex(interp, obj, indent, pad, arrays_inline, &ds); + retval = json_pretty(interp, obj, indent, nopadding, pad, arrays_inline, &ds); if (retval == TCL_OK) replace_tclobj(prettyString, Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds))); diff --git a/generic/rl_json.c b/generic/rl_json.c index b644dfd..1b83150 100644 --- a/generic/rl_json.c +++ b/generic/rl_json.c @@ -121,7 +121,6 @@ static const char *extension_str[] = { static int new_json_value_from_list(Tcl_Interp* interp, int objc, Tcl_Obj *const objv[], Tcl_Obj** res); static int NRforeach_next_loop_bottom(ClientData cdata[], Tcl_Interp* interp, int retcode); static int json_pretty_dbg(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, Tcl_DString* ds); -int json_pretty_ex(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, int arrays_inline, Tcl_DString* ds); static int _setdir(Tcl_Interp* interp) //{{{ { @@ -1340,7 +1339,7 @@ static int foreach(Tcl_Interp* interp, int objc, Tcl_Obj *const objv[], enum col } //}}} -int json_pretty_ex(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, int arrays_inline, Tcl_DString* ds) //{{{ +int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, int nopadding, Tcl_Obj* pad, int arrays_inline, Tcl_DString* ds) //{{{ { int pad_len, next_pad_len, count; enum json_types type; @@ -1378,15 +1377,19 @@ int json_pretty_ex(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* TEST_OK_LABEL(finally, retval, Tcl_DictObjFirst(interp, val, &search, &k, &v, &done)); - for (; !done; Tcl_DictObjNext(&search, &k, &v, &done)) { - Tcl_GetStringFromObj(k, &k_len); - if (k_len <= 20 && k_len > max) - max = k_len; - } - Tcl_DictObjDone(&search); + // keep the default behaviour, if wanted add the -nopadding option + // and the output will be condensed + if (!nopadding) { + for (; !done; Tcl_DictObjNext(&search, &k, &v, &done)) { + Tcl_GetStringFromObj(k, &k_len); + if (k_len <= 20 && k_len > max) + max = k_len; + } + Tcl_DictObjDone(&search); - if (max > 20) - max = 20; // If this cap is changed be sure to adjust the key_pad_buf length above + if (max > 20) + max = 20; // If this cap is changed be sure to adjust the key_pad_buf length above + } replace_tclobj(&next_pad, Tcl_DuplicateObj(pad)); Tcl_AppendObjToObj(next_pad, indent); @@ -1402,11 +1405,14 @@ int json_pretty_ex(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* append_json_string(&scx, k); Tcl_DStringAppend(ds, ": ", 2); - Tcl_GetStringFromObj(k, &k_len); - if (k_len < max) - Tcl_DStringAppend(ds, key_pad_buf, max-k_len); + // keep the default behaviour, if wanted add the -nopadding option + if (!nopadding) { + Tcl_GetStringFromObj(k, &k_len); + if (k_len < max) + Tcl_DStringAppend(ds, key_pad_buf, max-k_len); + } - if (json_pretty_ex(interp, v, indent, next_pad, arrays_inline, ds) != TCL_OK) { + if (json_pretty(interp, v, indent, nopadding, next_pad, arrays_inline, ds) != TCL_OK) { Tcl_DictObjDone(&search); retval = TCL_ERROR; goto finally; @@ -1451,7 +1457,7 @@ int json_pretty_ex(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* Tcl_DStringAppend(ds, "[", 1); count = 0; for (i=0; ijSON_Foreach) /* 32 */ #define JSON_Valid \ (rl_jsonStubsPtr->jSON_Valid) /* 33 */ -#define JSON_Pretty_Ex \ - (rl_jsonStubsPtr->jSON_Pretty_Ex) /* 34 */ #endif /* defined(USE_RL_JSON_STUBS) */ diff --git a/generic/rl_jsonInt.h b/generic/rl_jsonInt.h index beef560..7cb495d 100644 --- a/generic/rl_jsonInt.h +++ b/generic/rl_jsonInt.h @@ -252,8 +252,7 @@ int apply_template_actions(Tcl_Interp* interp, Tcl_Obj* template, Tcl_Obj* actio int build_template_actions(Tcl_Interp* interp, Tcl_Obj* template, Tcl_Obj** actions); int convert_to_tcl(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj** out); int resolve_path(Tcl_Interp* interp, Tcl_Obj* src, Tcl_Obj *const pathv[], int pathc, Tcl_Obj** target, const int exists, const int modifiers, Tcl_Obj* def); -int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, Tcl_DString* ds); -int json_pretty_ex(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, int arrays_inline, Tcl_DString* ds); +int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, int nopadding, Tcl_Obj* pad, int arrays_inline, Tcl_DString* ds); void foreach_state_free(struct foreach_state* state); #define TEMPLATE_TYPE(s, len, out) \ diff --git a/generic/rl_jsonStubInit.c b/generic/rl_jsonStubInit.c index 279fd55..d981bb0 100644 --- a/generic/rl_jsonStubInit.c +++ b/generic/rl_jsonStubInit.c @@ -38,8 +38,7 @@ const Rl_jsonStubs rl_jsonStubs = { JSON_Keys, /* 30 */ JSON_Decode, /* 31 */ JSON_Foreach, /* 32 */ - JSON_Valid, /* 33 */ - JSON_Pretty_Ex, /* 34 */ + JSON_Valid /* 33 */ }; /* !END!: Do not edit above this line. */ diff --git a/tests/pretty.test b/tests/pretty.test index c1407bf..162e5c2 100644 --- a/tests/pretty.test +++ b/tests/pretty.test @@ -6,7 +6,9 @@ if {"::tcltest" ni [namespace children]} { package require rl_json namespace path {::rl_json} -test pretty-1.1 {Basic pretty-print} -body { #<<< +test pretty-jsonPretty-1.1 {} -body {json pretty -indent} -returnCodes error -result {missing argument to "-indent"} -errorCode {TCL ARGUMENT MISSING} +#>>> +test pretty-1.2 {Basic pretty-print} -body { #<<< json pretty {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null,{"inner": "obj"}]}} } -cleanup { unset -nocomplain o @@ -28,7 +30,7 @@ test pretty-1.1 {Basic pretty-print} -body { #<<< ] }} #>>> -test pretty-1.2 {Basic pretty-print, different indent} -body { #<<< +test pretty-1.3 {Basic pretty-print, different indent} -body { #<<< json pretty -indent " " {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null,{"inner": "obj"}]}} } -result {{ "foo": null, @@ -48,11 +50,15 @@ test pretty-1.2 {Basic pretty-print, different indent} -body { #<<< ] }} #>>> -test pretty-2.1 {too few args} -body { #<<< +test pretty-jsonPretty-2.1 {} -body {json pretty -- 1} -result 1 +#>>> +test pretty-2.2 {too few args} -body { #<<< json pretty -} -returnCodes error -result {wrong # args: should be "pretty pretty ?-indent indent? ?-compact? ?-arrays inline|multiline? json_val ?key ...?"} +} -returnCodes error -result {wrong # args: should be "pretty pretty ?-indent indent? ?-compact? ?-nopadding? ?-arrays inline|multiline? json_val ?key ...?"} #>>> -test pretty-3.1 {path} -body { #<<< +test pretty-jsonPretty-3.1 {} -body {json pretty -indent { }} -returnCodes error -result {wrong # args: should be "pretty ?-indent indent? ?-compact? ?-nopadding? ?-arrays inline|multiline? json_val ?key ...?"} -errorCode {TCL WRONGARGS} +#>>> +test pretty-3.2 {path} -body { #<<< json pretty {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null,{"inner": "obj"}]}} {This is a much longer key} } -result {[ "str", @@ -66,43 +72,25 @@ test pretty-3.1 {path} -body { #<<< } ]} #>>> -test pretty-3.2 {bad path} -body { #<<< +test pretty-3.3 {bad path} -body { #<<< json pretty {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null,{"inner": "obj"}]}} bad } -returnCodes error -result {Path element 2: "bad" not found} -errorCode NONE #>>> - -test debug-1.1 {Basic debug pretty-print} -body { #<<< - json debug {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null,{"inner": "obj"}]}} -} -match regexp -result {^\(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\){ - "foo": \(0x[0-9a-fA-F]+\[[0-9]+\]+/NULL\)null, - "empty": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\){}, - "emptyarr": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)\[\], - "hello, world": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)"bar", - "This is a much longer key": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)\[ - \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)"str", - \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)123, - \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)123.4, - \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)true, - \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)false, - \(0x[0-9a-fA-F]+\[[0-9]+\]+/NULL\)null, - \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\){ - "inner": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)"obj" - } - \] -}$} +test pretty-jsonPretty-4.1 {} -body {json pretty bad} -returnCodes error -result {Error parsing JSON value: Illegal character at offset 0} -errorCode {RL JSON PARSE {Illegal character} bad 0} #>>> - -test pretty-4.1 {compact output} -body { +test pretty-4.2 {compact output} -body { json pretty -compact {{"foo":"bar","array":[1,2,3]}} } -result {{"foo":"bar","array":[1,2,3]}} - -test pretty-5.1 {arrays inline mode} -body { +#>>> +test pretty-jsonPretty-5.1 {} -body {json pretty -bad} -returnCodes error -result {bad option "-bad": must be *} -errorCode {TCL LOOKUP INDEX option -bad} -match glob +#>>> +test pretty-5.2 {arrays inline mode} -body { json pretty -arrays inline {{"arr":[1,2,3,4,5]}} } -result {{ "arr": [1,2,3,4,5] }} - -test pretty-5.2 {arrays multiline mode} -body { +#>>> +test pretty-5.3 {arrays multiline mode} -body { json pretty -arrays multiline {{"arr":[1,2,3]}} } -result {{ "arr": [ @@ -111,14 +99,14 @@ test pretty-5.2 {arrays multiline mode} -body { 3 ] }} - -test pretty-5.3 {arrays default auto mode - small inline} -body { +#>>> +test pretty-5.4 {arrays default auto mode - small inline} -body { json pretty {{"small":[1,2,3]}} } -result {{ "small": [1,2,3] }} - -test pretty-5.4 {arrays default auto mode - large multiline} -body { +#>>> +test pretty-5.5 {arrays default auto mode - large multiline} -body { json pretty {{"large":[1,2,3,4,5]}} } -result {{ "large": [ @@ -129,13 +117,13 @@ test pretty-5.4 {arrays default auto mode - large multiline} -body { 5 ] }} - +#>>> test pretty-6.1 {combine -indent with -arrays inline} -body { json pretty -indent " " -arrays inline {{"arr":[1,2,3,4,5]}} } -result {{ "arr": [1,2,3,4,5] }} - +#>>> test pretty-6.2 {combine -indent with -arrays multiline} -body { json pretty -indent " " -arrays multiline {{"arr":[1,2,3]}} } -result {{ @@ -145,13 +133,13 @@ test pretty-6.2 {combine -indent with -arrays multiline} -body { 3 ] }} - +#>>> test pretty-6.3 {tab indent with arrays inline} -body { json pretty -indent "\t" -arrays inline {{"data":[10,20,30]}} } -result {{ "data": [10,20,30] }} - +#>>> test pretty-6.4 {tab indent with arrays multiline} -body { json pretty -indent "\t" -arrays multiline {{"data":[10,20]}} } -result {{ @@ -160,23 +148,23 @@ test pretty-6.4 {tab indent with arrays multiline} -body { 20 ] }} - +#>>> test pretty-7.1 {compact overrides indent} -body { json pretty -compact -indent " " {{"foo":"bar"}} } -result {{"foo":"bar"}} - +#>>> test pretty-7.2 {compact overrides arrays inline} -body { json pretty -compact -arrays inline {{"arr":[1,2,3]}} } -result {{"arr":[1,2,3]}} - +#>>> test pretty-7.3 {compact overrides arrays multiline} -body { json pretty -compact -arrays multiline {{"arr":[1,2,3,4,5]}} } -result {{"arr":[1,2,3,4,5]}} - +#>>> test pretty-7.4 {compact with all options} -body { json pretty -compact -indent "\t" -arrays multiline {{"x":[1,2],"y":{"z":"w"}}} } -result {{"x":[1,2],"y":{"z":"w"}}} - +#>>> test pretty-8.1 {nested objects with custom indent} -body { json pretty -indent " " {{"outer":{"inner":{"deep":"value"}}}} } -result {{ @@ -186,7 +174,7 @@ test pretty-8.1 {nested objects with custom indent} -body { } } }} - +#>>> test pretty-8.2 {nested arrays with multiline} -body { json pretty -arrays multiline {{"matrix":[[1,2],[3,4]]}} } -result {{ @@ -201,13 +189,13 @@ test pretty-8.2 {nested arrays with multiline} -body { ] ] }} - +#>>> test pretty-8.3 {nested arrays with inline} -body { json pretty -arrays inline {{"matrix":[[1,2],[3,4]]}} } -result {{ "matrix": [[1,2],[3,4]] }} - +#>>> test pretty-8.4 {mixed nested structures} -body { json pretty -indent " " {{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}} } -result {{ @@ -219,31 +207,31 @@ test pretty-8.4 {mixed nested structures} -body { "age": 25 }] }} - +#>>> test pretty-9.1 {empty array with default} -body { json pretty {{"empty":[]}} } -result {{ "empty": [] }} - +#>>> test pretty-9.2 {empty array with arrays inline} -body { json pretty -arrays inline {{"empty":[]}} } -result {{ "empty": [] }} - +#>>> test pretty-9.3 {empty array with arrays multiline} -body { json pretty -arrays multiline {{"empty":[]}} } -result {{ "empty": [] }} - +#>>> test pretty-9.4 {empty object} -body { json pretty {{"empty":{}}} } -result {{ "empty": {} }} - +#>>> test pretty-10.1 {complex structure default formatting} -body { json pretty {{"api":{"version":"1.0","endpoints":["/user","/data","/login","/logout"]}}} } -result {{ @@ -257,7 +245,7 @@ test pretty-10.1 {complex structure default formatting} -body { ] } }} - +#>>> test pretty-10.2 {complex structure with indent and inline arrays} -body { json pretty -indent "\t" -arrays inline {{"config":{"debug":true,"ports":[8080,8081,8082],"name":"server"}}} } -result {{ @@ -267,7 +255,7 @@ test pretty-10.2 {complex structure with indent and inline arrays} -body { "name": "server" } }} - +#>>> test pretty-10.3 {multiple data types} -body { json pretty {{"string":"text","number":42,"bool":true,"null":null,"array":[1,2]}} } -result {{ @@ -277,19 +265,19 @@ test pretty-10.3 {multiple data types} -body { "null": null, "array": [1,2] }} - +#>>> test pretty-11.1 {single space indent} -body { json pretty -indent " " {{"a":"b"}} } -result {{ "a": "b" }} - +#>>> test pretty-11.2 {large indent} -body { json pretty -indent " " {{"x":"y"}} } -result {{ "x": "y" }} - +#>>> test pretty-11.3 {three space indent with nested} -body { json pretty -indent " " {{"outer":{"inner":"val"}}} } -result {{ @@ -297,13 +285,26 @@ test pretty-11.3 {three space indent with nested} -body { "inner": "val" } }} - -# Coverage golf -test pretty-jsonPretty-1.1 {} -body {json pretty -indent} -returnCodes error -result {missing argument to "-indent"} -errorCode {TCL ARGUMENT MISSING} -test pretty-jsonPretty-2.1 {} -body {json pretty -- 1} -result 1 -test pretty-jsonPretty-3.1 {} -body {json pretty -indent { }} -returnCodes error -result {wrong # args: should be "pretty ?-indent indent? ?-compact? ?-arrays inline|multiline? json_val ?key ...?"} -errorCode {TCL WRONGARGS} -test pretty-jsonPretty-4.1 {} -body {json pretty bad} -returnCodes error -result {Error parsing JSON value: Illegal character at offset 0} -errorCode {RL JSON PARSE {Illegal character} bad 0} -test pretty-jsonPretty-5.1 {} -body {json pretty -bad} -returnCodes error -result {bad option "-bad": must be *} -errorCode {TCL LOOKUP INDEX option -bad} -match glob +#>>> +test debug-1.1 {Basic debug pretty-print} -body { #<<< + json debug {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null,{"inner": "obj"}]}} +} -match regexp -result {^\(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\){ + "foo": \(0x[0-9a-fA-F]+\[[0-9]+\]+/NULL\)null, + "empty": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\){}, + "emptyarr": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)\[\], + "hello, world": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)"bar", + "This is a much longer key": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)\[ + \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)"str", + \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)123, + \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)123.4, + \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)true, + \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)false, + \(0x[0-9a-fA-F]+\[[0-9]+\]+/NULL\)null, + \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\){ + "inner": \(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\)"obj" + } + \] +}$} json free_cache