From 2d26a718c8d338dfaf2325e8548dfe3a67febb9d Mon Sep 17 00:00:00 2001 From: = Date: Wed, 4 Jun 2025 13:08:19 +0200 Subject: [PATCH 1/3] Add pretty printing --- serialize.c3 | 64 ++++++++++++++++++++++++++++++++++------------- serialize_test.c3 | 40 +++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 18 deletions(-) diff --git a/serialize.c3 b/serialize.c3 index 667a588..161534e 100644 --- a/serialize.c3 +++ b/serialize.c3 @@ -1,7 +1,9 @@ module json::serialize; import std::core; -macro void? add_json(src, DString* dest) { +const SPACES = 4; + +macro void? add_json(src, DString* dest, int indent = 0) { $if ($defined($typeof(src).to_json)): src.to_json(dest)!; return; @@ -53,7 +55,8 @@ macro void? add_json(src, DString* dest) { $endif // Not Maybe, just regular struct - dest.append("{"); + dest.append("{\n"); + indent++; int members_written; $foreach $member : $typeof(src).membersof: $if ($member.kindof == TypeKind.UNION): @@ -75,34 +78,59 @@ macro void? add_json(src, DString* dest) { // do nothing } else { if (members_written > 0) { - dest.append(","); + dest.append(",\n"); } - dest.appendf(`"%s":`, $member.nameof); - add_json(src.$eval($member.nameof), dest)!!; + for (int i = 0; i < indent * SPACES; i++) + { + dest.append(" "); + } + dest.appendf(`"%s": `, $member.nameof); + add_json(src.$eval($member.nameof), dest, indent)!!; members_written += 1; } $else if (members_written > 0) { - dest.append(","); + dest.append(",\n"); } - dest.appendf(`"%s":`, $member.nameof); - add_json(src.$eval($member.nameof), dest)!!; + for (int i = 0; i < indent * SPACES; i++) + { + dest.append(" "); + } + dest.appendf(`"%s": `, $member.nameof); + add_json(src.$eval($member.nameof), dest, indent)!!; members_written += 1; $endif $endif $endforeach - dest.append("}"); + dest.append("\n"); + indent--; + for (int i = 0; i < indent * SPACES; i++) + { + dest.append(" "); + } + dest.append("}"); return; $case TypeKind.SLICE: - dest.append("["); + dest.append("[\n"); + indent++; foreach (idx, element : src) { if (idx > 0) { - dest.append(","); + dest.append(",\n"); } - add_json(element, dest)!!; + for (int i = 0; i < indent * SPACES; i++) + { + dest.append(" "); + } + add_json(element, dest, indent)!!; } - dest.append("]"); - return; + dest.append("\n"); + indent--; + for (int i = 0; i < indent * SPACES; i++) + { + dest.append(" "); + } + dest.append("]"); + return; $case TypeKind.UNION: return UNION_NOT_SUPPORTED?; $endswitch @@ -117,7 +145,7 @@ macro void? add_hashmap(src, DString* dest) { return EXPECTED_HASHMAP?; $endif - dest.append("{"); + dest.append("{\n"); usz idx; var iter = src.iter(); while (idx < iter.len()) { @@ -125,14 +153,14 @@ macro void? add_hashmap(src, DString* dest) { var key = entry.key; var value = entry.value; if (idx > 0) { - dest.append(","); + dest.append(",\n"); } add_json(key, dest)!; - dest.append(":"); + dest.append(": "); add_json(value, dest)!; idx += 1; }; - dest.append("}"); + dest.append("\n}\n"); } faultdef UNION_NOT_SUPPORTED, EXPECTED_HASHMAP; diff --git a/serialize_test.c3 b/serialize_test.c3 index f88c893..ad1c2fd 100644 --- a/serialize_test.c3 +++ b/serialize_test.c3 @@ -2,6 +2,7 @@ module serialize_test; import std::collections; import std::core::test; import json; +import std::io; fn void test_basic() @test { test::eq(json::to_json(true, allocator::temp())!!, "true"); @@ -16,9 +17,48 @@ struct Foo { fn void test_struct() @test { Foo foo = { .is_bar = true, .baz = 1 }; + + io::printn("------------"); + io::printn(json::to_json(foo, mem)!!); + io::printn("------------"); + test::eq(json::to_json(foo, allocator::temp())!!, `{"is_bar":true,"baz":1}`); } +struct Complex +{ + String s; + String[] s1; + Foo[] f; +} + +fn void test_struct_2() @test +{ + Complex c = + { + .s = "Complex", + .s1 = + { + "Line1", + "Line2" + }, + .f = + { + { + true, + 5 + }, + { + false, 42 + } + } + }; + + io::printn(json::to_json(c, allocator::temp())!!); + + test::eq(json::to_json(c, allocator::temp())!!, "TEST"); +} + fn void test_array() @test { int[] xs = { 1, 2 }; test::eq(json::to_json(xs, allocator::temp())!!, "[1,2]"); From 9e4bf8d55f5a12bf8b9dcde553ca538b35928309 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 4 Jun 2025 14:33:52 +0200 Subject: [PATCH 2/3] Add pretty option & fixes --- json.c3 | 4 +- serialize.c3 | 102 ++++++++++++++++++++++++++++++---------------- serialize_test.c3 | 36 +--------------- 3 files changed, 72 insertions(+), 70 deletions(-) diff --git a/json.c3 b/json.c3 index 2b68801..7f660f4 100644 --- a/json.c3 +++ b/json.c3 @@ -14,11 +14,11 @@ macro from_str($Type, Allocator alloc, String s) { return deserialize::parse($Type, alloc, &lex); } -macro String? to_json(expr, Allocator alloc) { +macro String? to_json(expr, Allocator alloc, bool pretty = false) { @pool() { DString s; s.tinit(); - serialize::add_json(expr, &s)!; + serialize::add_json(expr, &s, pretty)!; return s.copy_str(alloc); }; } diff --git a/serialize.c3 b/serialize.c3 index 161534e..aefd040 100644 --- a/serialize.c3 +++ b/serialize.c3 @@ -1,11 +1,11 @@ module json::serialize; -import std::core; +import std::core, std::io; const SPACES = 4; -macro void? add_json(src, DString* dest, int indent = 0) { +macro void? add_json(src, DString* dest, bool pretty = false, int indent = 0) { $if ($defined($typeof(src).to_json)): - src.to_json(dest)!; + src.to_json(dest, pretty, indent)!; return; $endif @@ -42,7 +42,7 @@ macro void? add_json(src, DString* dest, int indent = 0) { // Check Maybe first $if ($typeof(src).nameof == "Maybe"): if (src.has_value) { - add_json(src.$eval($typeof(src).membersof[0].nameof), dest)!!; + add_json(src.$eval($typeof(src).membersof[0].nameof), dest, pretty, indent)!!; } else { dest.appendf("null"); } @@ -50,12 +50,13 @@ macro void? add_json(src, DString* dest, int indent = 0) { $endif $if ($typeof(src).nameof == "HashMap"): - add_hashmap(src, dest)!!; + add_hashmap(src, dest, pretty, indent)!!; return; $endif // Not Maybe, just regular struct - dest.append("{\n"); + dest.append("{"); + if (pretty) dest.append("\n"); indent++; int members_written; $foreach $member : $typeof(src).membersof: @@ -78,56 +79,77 @@ macro void? add_json(src, DString* dest, int indent = 0) { // do nothing } else { if (members_written > 0) { - dest.append(",\n"); + dest.append(","); + if (pretty) dest.append("\n"); } - for (int i = 0; i < indent * SPACES; i++) + if (pretty) { - dest.append(" "); + for (int i = 0; i < indent * SPACES; i++) + { + dest.append(" "); + } } - dest.appendf(`"%s": `, $member.nameof); - add_json(src.$eval($member.nameof), dest, indent)!!; + dest.appendf(`"%s":`, $member.nameof); + if (pretty) dest.append(" "); + add_json(src.$eval($member.nameof), dest, pretty, indent)!!; members_written += 1; } $else if (members_written > 0) { - dest.append(",\n"); + dest.append(","); + if (pretty) dest.append("\n"); } - for (int i = 0; i < indent * SPACES; i++) + if (pretty) { - dest.append(" "); + for (int i = 0; i < indent * SPACES; i++) + { + dest.append(" "); + } } - dest.appendf(`"%s": `, $member.nameof); - add_json(src.$eval($member.nameof), dest, indent)!!; + dest.appendf(`"%s":`, $member.nameof); + if (pretty) dest.append(" "); + add_json(src.$eval($member.nameof), dest, pretty, indent)!!; members_written += 1; $endif $endif $endforeach - dest.append("\n"); + if (pretty) dest.append("\n"); indent--; - for (int i = 0; i < indent * SPACES; i++) + if (pretty) { - dest.append(" "); + for (int i = 0; i < indent * SPACES; i++) + { + dest.append(" "); + } } dest.append("}"); return; $case TypeKind.SLICE: - dest.append("[\n"); + dest.append("["); + if (pretty) dest.append("\n"); indent++; foreach (idx, element : src) { if (idx > 0) { - dest.append(",\n"); + dest.append(","); + if (pretty) dest.append("\n"); } - for (int i = 0; i < indent * SPACES; i++) + if (pretty) { - dest.append(" "); + for (int i = 0; i < indent * SPACES; i++) + { + dest.append(" "); + } } - add_json(element, dest, indent)!!; + add_json(element, dest, pretty, indent)!!; } - dest.append("\n"); + if (pretty) dest.append("\n"); indent--; - for (int i = 0; i < indent * SPACES; i++) + if (pretty) { - dest.append(" "); + for (int i = 0; i < indent * SPACES; i++) + { + dest.append(" "); + } } dest.append("]"); return; @@ -140,12 +162,13 @@ macro void? add_json(src, DString* dest, int indent = 0) { // Using iter; with the @each_entry, didn't like using var // in the macro body call. -macro void? add_hashmap(src, DString* dest) { +macro void? add_hashmap(src, DString* dest, bool pretty, int indent) { $if ($typeof(src).nameof != "HashMap"): return EXPECTED_HASHMAP?; $endif - dest.append("{\n"); + dest.append("{"); + if (pretty) dest.append("\n"); usz idx; var iter = src.iter(); while (idx < iter.len()) { @@ -153,14 +176,25 @@ macro void? add_hashmap(src, DString* dest) { var key = entry.key; var value = entry.value; if (idx > 0) { - dest.append(",\n"); + dest.append(","); + if (pretty) dest.append("\n"); } - add_json(key, dest)!; - dest.append(": "); - add_json(value, dest)!; + if (pretty) + { + for (int i = 0; i < indent * SPACES; i++) + { + dest.append(" "); + } + } + add_json(key, dest, pretty, indent)!; + dest.append(":"); + if (pretty) dest.append(" "); + add_json(value, dest, pretty, indent)!; idx += 1; }; - dest.append("\n}\n"); + if (pretty) dest.append("\n"); + dest.append("}"); + if (pretty) dest.append("\n"); } faultdef UNION_NOT_SUPPORTED, EXPECTED_HASHMAP; diff --git a/serialize_test.c3 b/serialize_test.c3 index ad1c2fd..2528343 100644 --- a/serialize_test.c3 +++ b/serialize_test.c3 @@ -17,11 +17,6 @@ struct Foo { fn void test_struct() @test { Foo foo = { .is_bar = true, .baz = 1 }; - - io::printn("------------"); - io::printn(json::to_json(foo, mem)!!); - io::printn("------------"); - test::eq(json::to_json(foo, allocator::temp())!!, `{"is_bar":true,"baz":1}`); } @@ -32,33 +27,6 @@ struct Complex Foo[] f; } -fn void test_struct_2() @test -{ - Complex c = - { - .s = "Complex", - .s1 = - { - "Line1", - "Line2" - }, - .f = - { - { - true, - 5 - }, - { - false, 42 - } - } - }; - - io::printn(json::to_json(c, allocator::temp())!!); - - test::eq(json::to_json(c, allocator::temp())!!, "TEST"); -} - fn void test_array() @test { int[] xs = { 1, 2 }; test::eq(json::to_json(xs, allocator::temp())!!, "[1,2]"); @@ -134,9 +102,9 @@ struct StructWithUnionCustom { Foo foo; } } -fn void? StructWithUnionCustom.to_json(self, DString* dest) { +fn void? StructWithUnionCustom.to_json(self, DString* dest, bool pretty, int indent) { // Unions not supported, must handle each variant manually - serialize::add_json(self.foo, dest)!; + serialize::add_json(self.foo, dest, pretty, indent)!; } fn void test_struct_with_union_custom() @test { StructWithUnionCustom s = { .foo = { .is_bar = true} }; From 6bccb8b50efd12e25d11899cd622961729d786fe Mon Sep 17 00:00:00 2001 From: = Date: Thu, 5 Jun 2025 14:49:59 +0200 Subject: [PATCH 3/3] Make spaces configurable --- json.c3 | 4 ++-- serialize.c3 | 36 +++++++++++++++++------------------- serialize_test.c3 | 7 +++---- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/json.c3 b/json.c3 index 7f660f4..ff7ec56 100644 --- a/json.c3 +++ b/json.c3 @@ -14,11 +14,11 @@ macro from_str($Type, Allocator alloc, String s) { return deserialize::parse($Type, alloc, &lex); } -macro String? to_json(expr, Allocator alloc, bool pretty = false) { +macro String? to_json(expr, Allocator alloc, bool pretty = false, int spaces = 4) { @pool() { DString s; s.tinit(); - serialize::add_json(expr, &s, pretty)!; + serialize::add_json(expr, &s, pretty, spaces)!; return s.copy_str(alloc); }; } diff --git a/serialize.c3 b/serialize.c3 index aefd040..50488ff 100644 --- a/serialize.c3 +++ b/serialize.c3 @@ -1,11 +1,9 @@ module json::serialize; -import std::core, std::io; +import std::core; -const SPACES = 4; - -macro void? add_json(src, DString* dest, bool pretty = false, int indent = 0) { +macro void? add_json(src, DString* dest, bool pretty = false, int spaces = 4, int indent = 0) { $if ($defined($typeof(src).to_json)): - src.to_json(dest, pretty, indent)!; + src.to_json(dest, pretty, spaces, indent)!; return; $endif @@ -42,7 +40,7 @@ macro void? add_json(src, DString* dest, bool pretty = false, int indent = 0) { // Check Maybe first $if ($typeof(src).nameof == "Maybe"): if (src.has_value) { - add_json(src.$eval($typeof(src).membersof[0].nameof), dest, pretty, indent)!!; + add_json(src.$eval($typeof(src).membersof[0].nameof), dest, pretty, spaces, indent)!!; } else { dest.appendf("null"); } @@ -50,7 +48,7 @@ macro void? add_json(src, DString* dest, bool pretty = false, int indent = 0) { $endif $if ($typeof(src).nameof == "HashMap"): - add_hashmap(src, dest, pretty, indent)!!; + add_hashmap(src, dest, pretty, spaces, indent)!!; return; $endif @@ -84,14 +82,14 @@ macro void? add_json(src, DString* dest, bool pretty = false, int indent = 0) { } if (pretty) { - for (int i = 0; i < indent * SPACES; i++) + for (int i = 0; i < indent * spaces; i++) { dest.append(" "); } } dest.appendf(`"%s":`, $member.nameof); if (pretty) dest.append(" "); - add_json(src.$eval($member.nameof), dest, pretty, indent)!!; + add_json(src.$eval($member.nameof), dest, pretty, spaces, indent)!!; members_written += 1; } $else @@ -101,14 +99,14 @@ macro void? add_json(src, DString* dest, bool pretty = false, int indent = 0) { } if (pretty) { - for (int i = 0; i < indent * SPACES; i++) + for (int i = 0; i < indent * spaces; i++) { dest.append(" "); } } dest.appendf(`"%s":`, $member.nameof); if (pretty) dest.append(" "); - add_json(src.$eval($member.nameof), dest, pretty, indent)!!; + add_json(src.$eval($member.nameof), dest, pretty, spaces, indent)!!; members_written += 1; $endif $endif @@ -117,7 +115,7 @@ macro void? add_json(src, DString* dest, bool pretty = false, int indent = 0) { indent--; if (pretty) { - for (int i = 0; i < indent * SPACES; i++) + for (int i = 0; i < indent * spaces; i++) { dest.append(" "); } @@ -135,18 +133,18 @@ macro void? add_json(src, DString* dest, bool pretty = false, int indent = 0) { } if (pretty) { - for (int i = 0; i < indent * SPACES; i++) + for (int i = 0; i < indent * spaces; i++) { dest.append(" "); } } - add_json(element, dest, pretty, indent)!!; + add_json(element, dest, pretty, spaces, indent)!!; } if (pretty) dest.append("\n"); indent--; if (pretty) { - for (int i = 0; i < indent * SPACES; i++) + for (int i = 0; i < indent * spaces; i++) { dest.append(" "); } @@ -162,7 +160,7 @@ macro void? add_json(src, DString* dest, bool pretty = false, int indent = 0) { // Using iter; with the @each_entry, didn't like using var // in the macro body call. -macro void? add_hashmap(src, DString* dest, bool pretty, int indent) { +macro void? add_hashmap(src, DString* dest, bool pretty, int spaces, int indent) { $if ($typeof(src).nameof != "HashMap"): return EXPECTED_HASHMAP?; $endif @@ -181,15 +179,15 @@ macro void? add_hashmap(src, DString* dest, bool pretty, int indent) { } if (pretty) { - for (int i = 0; i < indent * SPACES; i++) + for (int i = 0; i < indent * spaces; i++) { dest.append(" "); } } - add_json(key, dest, pretty, indent)!; + add_json(key, dest, pretty, spaces, indent)!; dest.append(":"); if (pretty) dest.append(" "); - add_json(value, dest, pretty, indent)!; + add_json(value, dest, pretty, spaces, indent)!; idx += 1; }; if (pretty) dest.append("\n"); diff --git a/serialize_test.c3 b/serialize_test.c3 index 2528343..dbe59b4 100644 --- a/serialize_test.c3 +++ b/serialize_test.c3 @@ -2,7 +2,6 @@ module serialize_test; import std::collections; import std::core::test; import json; -import std::io; fn void test_basic() @test { test::eq(json::to_json(true, allocator::temp())!!, "true"); @@ -17,7 +16,7 @@ struct Foo { fn void test_struct() @test { Foo foo = { .is_bar = true, .baz = 1 }; - test::eq(json::to_json(foo, allocator::temp())!!, `{"is_bar":true,"baz":1}`); + test::eq(json::to_json(foo, allocator::temp())!!, `{"is_bar":true,"baz":1}`); } struct Complex @@ -102,9 +101,9 @@ struct StructWithUnionCustom { Foo foo; } } -fn void? StructWithUnionCustom.to_json(self, DString* dest, bool pretty, int indent) { +fn void? StructWithUnionCustom.to_json(self, DString* dest, bool pretty, int spaces, int indent) { // Unions not supported, must handle each variant manually - serialize::add_json(self.foo, dest, pretty, indent)!; + serialize::add_json(self.foo, dest, pretty, spaces, indent)!; } fn void test_struct_with_union_custom() @test { StructWithUnionCustom s = { .foo = { .is_bar = true} };