jsonfmt: Add pretty option

That completes the json_format() function for now. At least, it now does
everything I had planned for it.

Ended up at a bit over 300 LOC. That's larger than I had expected, but
still alright.
This commit is contained in:
Yorhel 2025-01-30 07:52:22 +01:00
parent 1a0fb03205
commit aebe5a93dc
3 changed files with 56 additions and 8 deletions

View file

@ -2,10 +2,19 @@ typedef struct {
fustr out;
UV depth;
int canon;
int pretty; /* <0 when disabled, current nesting level otherwise */
} fujson_fmt_ctx;
static void fujson_fmt(pTHX_ fujson_fmt_ctx *, SV *);
static void fujson_fmt_indent(pTHX_ fujson_fmt_ctx *ctx) {
if (ctx->pretty >= 0) {
char *buf = fustr_write_buf(&ctx->out, 1 + ctx->pretty*3);
*buf = '\n';
memset(buf+1, ' ', ctx->pretty*3);
}
}
static void fujson_fmt_str(pTHX_ fujson_fmt_ctx *ctx, const char *stri, size_t len, int utf8) {
size_t off = 0, loff;
const unsigned char *str = (const unsigned char *)stri;
@ -118,12 +127,16 @@ static void fujson_fmt_int(pTHX_ fujson_fmt_ctx *ctx, SV *val) {
static void fujson_fmt_av(pTHX_ fujson_fmt_ctx *ctx, AV *av) {
int i, len = av_count(av);
fustr_write(&ctx->out, "[", 1);
ctx->pretty++;
for (i=0; i<len; i++) {
if (i) fustr_write(&ctx->out, ",", 1);
fujson_fmt_indent(aTHX_ ctx);
SV **sv = av_fetch(av, i, 0);
if (sv) fujson_fmt(aTHX_ ctx, *sv); /* sv will have magic if av is tied, but fujson_fmt() handles that. */
else fustr_write(&ctx->out, "null", 4);
}
ctx->pretty--;
if (i) fujson_fmt_indent(aTHX_ ctx);
fustr_write(&ctx->out, "]", 1);
}
@ -148,9 +161,11 @@ static int fujson_fmt_hvcmp(const void *pa, const void *pb) {
static void fujson_fmt_hvkv(pTHX_ fujson_fmt_ctx *ctx, HV *hv, HE *he, char **hestr) {
STRLEN helen;
if (*hestr) fustr_write(&ctx->out, ",", 1);
fujson_fmt_indent(aTHX_ ctx);
*hestr = HePV(he, helen);
fujson_fmt_str(aTHX_ ctx, *hestr, helen, HeUTF8(he));
fustr_write(&ctx->out, ":", 1);
if (ctx->pretty > 0) fustr_write(&ctx->out, " : ", 3);
else fustr_write(&ctx->out, ":", 1);
fujson_fmt(aTHX_ ctx, UNLIKELY(SvMAGICAL(hv)) ? hv_iterval(hv, he) : HeVAL(he));
}
@ -160,6 +175,7 @@ static void fujson_fmt_hv(pTHX_ fujson_fmt_ctx *ctx, HV *hv) {
int numkeys = hv_iterinit(hv);
fustr_write(&ctx->out, "{", 1);
ctx->pretty++;
/* Canonical order on tied hashes is not supported. Cpanel::JSON::XS has
* code to deal with that case and it's absolutely horrifying. */
@ -187,6 +203,8 @@ static void fujson_fmt_hv(pTHX_ fujson_fmt_ctx *ctx, HV *hv) {
} else {
while ((he = hv_iternext(hv))) fujson_fmt_hvkv(aTHX_ ctx, hv, he, &hestr);
}
ctx->pretty--;
if (hestr) fujson_fmt_indent(aTHX_ ctx);
fustr_write(&ctx->out, "}", 1);
}
@ -269,6 +287,7 @@ static SV *fujson_fmt_xs(pTHX_ I32 ax, I32 argc, SV *val) {
ctx.out.maxlen = 0;
ctx.depth = 0;
ctx.pretty = INT_MIN;
ctx.canon = 0;
while (i < argc) {
arg = SvPV_nolen(ST(i));
@ -278,6 +297,7 @@ static SV *fujson_fmt_xs(pTHX_ I32 ax, I32 argc, SV *val) {
i++;
if (strcmp(arg, "canonical") == 0) ctx.canon = SvPVXtrue(r);
else if (strcmp(arg, "pretty") == 0) ctx.pretty = SvPVXtrue(r) ? 0 : INT_MIN;
else if (strcmp(arg, "utf8") == 0) encutf8 = SvPVXtrue(r);
else if (strcmp(arg, "max_size") == 0) ctx.out.maxlen = SvUV(r);
else if (strcmp(arg, "max_depth") == 0) ctx.depth = SvUV(r);
@ -288,9 +308,8 @@ static SV *fujson_fmt_xs(pTHX_ I32 ax, I32 argc, SV *val) {
fustr_init(&ctx.out, 128, ctx.out.maxlen);
fujson_fmt(aTHX_ &ctx, val);
if (ctx.pretty >= 0) fustr_write(&ctx.out, "\n", 1);
r = fustr_done(&ctx.out);
if (!encutf8) SvUTF8_on(r);
return r;
}
/* TODO: pretty */