Add functions to print and to convert to string
This commit is contained in:
parent
5357d0a593
commit
438e800f2f
280
src/json.zig
280
src/json.zig
@ -1,4 +1,5 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub const JSONError = error{
|
||||
IncorrectType,
|
||||
@ -16,17 +17,43 @@ pub const JSONType = enum {
|
||||
Null,
|
||||
};
|
||||
|
||||
pub const JSONFormat = struct {
|
||||
indent_level: usize = 4,
|
||||
indent_char: u8 = ' ',
|
||||
one_line: bool = false,
|
||||
};
|
||||
|
||||
fn printIndent(writer: std.fs.File.Writer, format: JSONFormat, level: usize) !void {
|
||||
if (!format.one_line) {
|
||||
try writer.writeByte('\n');
|
||||
}
|
||||
|
||||
for (0..level) |_| {
|
||||
try writer.writeByteNTimes(format.indent_char, format.indent_level);
|
||||
}
|
||||
}
|
||||
|
||||
fn indentString(allocator: std.mem.Allocator, format: JSONFormat, level: usize) ![]const u8 {
|
||||
var str = std.ArrayList(u8).init(allocator);
|
||||
|
||||
if (!format.one_line) {
|
||||
try str.append('\n');
|
||||
}
|
||||
|
||||
for (0..level) |_| {
|
||||
try str.appendNTimes(format.indent_char, format.indent_level);
|
||||
}
|
||||
|
||||
return try str.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub const JSONObject = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
children: MapType,
|
||||
|
||||
const MapType = std.StringHashMap(*JSONValue);
|
||||
|
||||
pub fn get(self: *JSONObject, name: []const u8) ?*JSONValue {
|
||||
return self.children.get(name);
|
||||
}
|
||||
|
||||
fn init(allocator: std.mem.Allocator) JSONError!*JSONObject {
|
||||
pub fn create(allocator: std.mem.Allocator) JSONError!*JSONObject {
|
||||
var self = try allocator.create(JSONObject);
|
||||
self.allocator = allocator;
|
||||
self.children = MapType.init(allocator);
|
||||
@ -34,6 +61,59 @@ pub const JSONObject = struct {
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn get(self: *JSONObject, name: []const u8) ?*JSONValue {
|
||||
return self.children.get(name);
|
||||
}
|
||||
|
||||
pub fn print(self: *JSONObject, writer: std.fs.File.Writer, format: JSONFormat, indent_level: usize) std.fs.File.WriteError!void {
|
||||
try writer.writeByte('{');
|
||||
|
||||
var iter = self.children.keyIterator();
|
||||
var first = true;
|
||||
while (iter.next()) |key| {
|
||||
if (!first) {
|
||||
try writer.writeAll(", ");
|
||||
}
|
||||
|
||||
try printIndent(writer, format, indent_level + 1);
|
||||
try writer.print("\"{s}\": ", .{key.*});
|
||||
|
||||
try self.children.get(key.*).?.print(writer, format, indent_level + 1);
|
||||
first = false;
|
||||
}
|
||||
|
||||
try printIndent(writer, format, indent_level);
|
||||
try writer.writeByte('}');
|
||||
}
|
||||
|
||||
pub fn toString(self: *JSONObject, allocator: std.mem.Allocator, format: JSONFormat, indent_level: usize) std.mem.Allocator.Error![]const u8 {
|
||||
var str = std.ArrayList(u8).init(allocator);
|
||||
try str.append('{');
|
||||
|
||||
var iter = self.children.keyIterator();
|
||||
var first = true;
|
||||
while (iter.next()) |key| {
|
||||
if (!first) {
|
||||
try str.appendSlice(", ");
|
||||
}
|
||||
|
||||
try str.appendSlice(try indentString(allocator, format, indent_level + 1));
|
||||
try str.append('"');
|
||||
try str.appendSlice(key.*);
|
||||
try str.appendSlice("\": ");
|
||||
|
||||
const valstr = try self.children.get(key.*).?.toString(allocator, format, indent_level + 1);
|
||||
defer allocator.free(valstr);
|
||||
try str.appendSlice(valstr);
|
||||
first = false;
|
||||
}
|
||||
|
||||
try str.appendSlice(try indentString(allocator, format, indent_level));
|
||||
try str.append('}');
|
||||
|
||||
return try str.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn deinit(self: *JSONObject) void {
|
||||
var iter = self.children.valueIterator();
|
||||
while (iter.next()) |child| {
|
||||
@ -41,9 +121,10 @@ pub const JSONObject = struct {
|
||||
}
|
||||
|
||||
var names = self.children.keyIterator();
|
||||
while (names.next()) |name| {
|
||||
self.allocator.free(name.*);
|
||||
while (names.next()) |key| {
|
||||
self.allocator.free(key.*);
|
||||
}
|
||||
|
||||
self.children.deinit();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
@ -63,6 +144,9 @@ pub const JSONValue = struct {
|
||||
getBool: *const fn (self: *const JSONValue) JSONError!bool = incorrectValue(bool),
|
||||
|
||||
deinit: *const fn (self: *JSONValue) void,
|
||||
|
||||
print: *const fn (self: *const JSONValue, writer: std.fs.File.Writer, format: JSONFormat, indent_level: usize) std.fs.File.WriteError!void,
|
||||
toString: *const fn (self: *const JSONValue, allocator: std.mem.Allocator, format: JSONFormat, indent_level: usize) std.mem.Allocator.Error![]const u8,
|
||||
};
|
||||
|
||||
fn incorrectValue(comptime T: type) *const fn (*const JSONValue) JSONError!T {
|
||||
@ -97,34 +181,50 @@ pub const JSONValue = struct {
|
||||
return self.vtable.getObject(self);
|
||||
}
|
||||
|
||||
pub fn print(self: *const JSONValue, writer: std.fs.File.Writer, format: JSONFormat, indent_level: usize) std.fs.File.WriteError!void {
|
||||
return self.vtable.print(self, writer, format, indent_level);
|
||||
}
|
||||
|
||||
pub fn toString(self: *const JSONValue, allocator: std.mem.Allocator, format: JSONFormat, indent_level: usize) std.mem.Allocator.Error![]const u8 {
|
||||
return self.vtable.toString(self, allocator, format, indent_level);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *JSONValue) void {
|
||||
self.vtable.deinit(self);
|
||||
}
|
||||
|
||||
fn createNull(allocator: std.mem.Allocator) JSONError!*JSONValue {
|
||||
pub fn createNull(allocator: std.mem.Allocator) JSONError!*JSONValue {
|
||||
var self = try allocator.create(JSONValue);
|
||||
self.type = .Null;
|
||||
self.vtable = .{ .deinit = basicDeinit };
|
||||
self.vtable = .{ .deinit = basicDeinit, .print = printNull, .toString = toStringNull };
|
||||
self.allocator = allocator;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
fn printNull(_: *const JSONValue, writer: std.fs.File.Writer, _: JSONFormat, _: usize) std.fs.File.WriteError!void {
|
||||
return writer.writeAll("null");
|
||||
}
|
||||
|
||||
fn toStringNull(_: *const JSONValue, allocator: std.mem.Allocator, _: JSONFormat, _: usize) ![]const u8 {
|
||||
return allocator.dupe(u8, "null");
|
||||
}
|
||||
|
||||
fn basicDeinit(self: *JSONValue) void {
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
const JSONArrayValue = struct {
|
||||
pub const JSONArrayValue = struct {
|
||||
value: JSONValue,
|
||||
array: []*JSONValue,
|
||||
|
||||
fn init(allocator: std.mem.Allocator, array: []*JSONValue) !*JSONValue {
|
||||
pub fn create(allocator: std.mem.Allocator, array: []*JSONValue) !*JSONValue {
|
||||
var self = try allocator.create(JSONArrayValue);
|
||||
self.array = array;
|
||||
self.value = .{
|
||||
.type = .Array,
|
||||
.vtable = .{ .getArray = getValue, .deinit = deinit },
|
||||
.vtable = .{ .getArray = getValue, .print = print, .toString = toString, .deinit = deinit },
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
@ -136,6 +236,47 @@ const JSONArrayValue = struct {
|
||||
return self.array;
|
||||
}
|
||||
|
||||
fn print(value: *const JSONValue, writer: std.fs.File.Writer, format: JSONFormat, indent_level: usize) std.fs.File.WriteError!void {
|
||||
const self: *const JSONArrayValue = @fieldParentPtr("value", value);
|
||||
|
||||
try writer.writeAll("[ ");
|
||||
|
||||
var first = true;
|
||||
for (self.array) |val| {
|
||||
if (!first) {
|
||||
try writer.writeAll(", ");
|
||||
}
|
||||
|
||||
try val.print(writer, format, indent_level);
|
||||
first = false;
|
||||
}
|
||||
|
||||
try writer.writeAll(" ]");
|
||||
}
|
||||
|
||||
fn toString(value: *const JSONValue, allocator: std.mem.Allocator, format: JSONFormat, indent_level: usize) std.mem.Allocator.Error![]const u8 {
|
||||
const self: *const JSONArrayValue = @fieldParentPtr("value", value);
|
||||
var str = std.ArrayList(u8).init(allocator);
|
||||
|
||||
try str.appendSlice("[ ");
|
||||
|
||||
var first = true;
|
||||
for (self.array) |val| {
|
||||
if (!first) {
|
||||
try str.appendSlice(", ");
|
||||
}
|
||||
|
||||
const valstr = try val.toString(allocator, format, indent_level);
|
||||
defer allocator.free(valstr);
|
||||
try str.appendSlice(valstr);
|
||||
first = false;
|
||||
}
|
||||
|
||||
try str.appendSlice(" ]");
|
||||
|
||||
return str.toOwnedSlice();
|
||||
}
|
||||
|
||||
fn deinit(value: *JSONValue) void {
|
||||
var self: *JSONArrayValue = @fieldParentPtr("value", value);
|
||||
for (self.array) |val| {
|
||||
@ -146,16 +287,16 @@ const JSONArrayValue = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const JSONObjectValue = struct {
|
||||
pub const JSONObjectValue = struct {
|
||||
value: JSONValue,
|
||||
object: *JSONObject,
|
||||
|
||||
fn init(allocator: std.mem.Allocator, object: *JSONObject) !*JSONValue {
|
||||
pub fn create(allocator: std.mem.Allocator, object: *JSONObject) !*JSONValue {
|
||||
var self = try allocator.create(JSONObjectValue);
|
||||
self.object = object;
|
||||
self.value = .{
|
||||
.type = .Object,
|
||||
.vtable = .{ .getObject = getValue, .deinit = deinit },
|
||||
.vtable = .{ .getObject = getValue, .print = print, .toString = toString, .deinit = deinit },
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
@ -167,6 +308,16 @@ const JSONObjectValue = struct {
|
||||
return self.object;
|
||||
}
|
||||
|
||||
fn print(value: *const JSONValue, writer: std.fs.File.Writer, format: JSONFormat, indent_level: usize) std.fs.File.WriteError!void {
|
||||
const self: *const JSONObjectValue = @fieldParentPtr("value", value);
|
||||
try self.object.print(writer, format, indent_level);
|
||||
}
|
||||
|
||||
fn toString(value: *const JSONValue, allocator: std.mem.Allocator, format: JSONFormat, indent_level: usize) std.mem.Allocator.Error![]const u8 {
|
||||
const self: *const JSONObjectValue = @fieldParentPtr("value", value);
|
||||
return self.object.toString(allocator, format, indent_level);
|
||||
}
|
||||
|
||||
fn deinit(value: *JSONValue) void {
|
||||
var self: *const JSONObjectValue = @fieldParentPtr("value", value);
|
||||
self.object.deinit();
|
||||
@ -174,16 +325,16 @@ const JSONObjectValue = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const JSONIntValue = struct {
|
||||
pub const JSONIntValue = struct {
|
||||
value: JSONValue,
|
||||
int: isize,
|
||||
|
||||
fn init(allocator: std.mem.Allocator, value: isize) !*JSONValue {
|
||||
pub fn create(allocator: std.mem.Allocator, value: isize) !*JSONValue {
|
||||
var self = try allocator.create(JSONIntValue);
|
||||
self.int = value;
|
||||
self.value = .{
|
||||
.type = .Int,
|
||||
.vtable = .{ .getInt = getValue, .deinit = deinit },
|
||||
.vtable = .{ .getInt = getValue, .print = print, .toString = toString, .deinit = deinit },
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
@ -195,22 +346,32 @@ const JSONIntValue = struct {
|
||||
return self.int;
|
||||
}
|
||||
|
||||
fn print(value: *const JSONValue, writer: std.fs.File.Writer, _: JSONFormat, _: usize) std.fs.File.WriteError!void {
|
||||
const self: *const JSONIntValue = @fieldParentPtr("value", value);
|
||||
try writer.print("{d}", .{self.int});
|
||||
}
|
||||
|
||||
fn toString(value: *const JSONValue, allocator: std.mem.Allocator, _: JSONFormat, _: usize) std.mem.Allocator.Error![]const u8 {
|
||||
const self: *const JSONIntValue = @fieldParentPtr("value", value);
|
||||
return std.fmt.allocPrint(allocator, "{d}", .{self.int});
|
||||
}
|
||||
|
||||
fn deinit(value: *JSONValue) void {
|
||||
var self: *JSONIntValue = @fieldParentPtr("value", value);
|
||||
self.value.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
const JSONFloatValue = struct {
|
||||
pub const JSONFloatValue = struct {
|
||||
value: JSONValue,
|
||||
float: f64,
|
||||
|
||||
fn init(allocator: std.mem.Allocator, value: f64) !*JSONValue {
|
||||
pub fn create(allocator: std.mem.Allocator, value: f64) !*JSONValue {
|
||||
var self = try allocator.create(JSONFloatValue);
|
||||
self.float = value;
|
||||
self.value = .{
|
||||
.type = .Float,
|
||||
.vtable = .{ .getFloat = getValue, .deinit = deinit },
|
||||
.vtable = .{ .getFloat = getValue, .print = print, .toString = toString, .deinit = deinit },
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
@ -222,22 +383,32 @@ const JSONFloatValue = struct {
|
||||
return self.float;
|
||||
}
|
||||
|
||||
fn print(value: *const JSONValue, writer: std.fs.File.Writer, _: JSONFormat, _: usize) std.fs.File.WriteError!void {
|
||||
const self: *const JSONFloatValue = @fieldParentPtr("value", value);
|
||||
try writer.print("{d}", .{self.float});
|
||||
}
|
||||
|
||||
fn toString(value: *const JSONValue, allocator: std.mem.Allocator, _: JSONFormat, _: usize) std.mem.Allocator.Error![]const u8 {
|
||||
const self: *const JSONFloatValue = @fieldParentPtr("value", value);
|
||||
return std.fmt.allocPrint(allocator, "{d}", .{self.float});
|
||||
}
|
||||
|
||||
fn deinit(value: *JSONValue) void {
|
||||
var self: *JSONIntValue = @fieldParentPtr("value", value);
|
||||
var self: *const JSONFloatValue = @fieldParentPtr("value", value);
|
||||
self.value.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
const JSONBoolValue = struct {
|
||||
pub const JSONBoolValue = struct {
|
||||
value: JSONValue,
|
||||
boolean: bool,
|
||||
|
||||
fn init(allocator: std.mem.Allocator, value: bool) !*JSONValue {
|
||||
pub fn create(allocator: std.mem.Allocator, value: bool) !*JSONValue {
|
||||
var self = try allocator.create(JSONBoolValue);
|
||||
self.boolean = value;
|
||||
self.value = .{
|
||||
.type = .Bool,
|
||||
.vtable = .{ .getBool = getValue, .deinit = deinit },
|
||||
.vtable = .{ .getBool = getValue, .print = print, .toString = toString, .deinit = deinit },
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
@ -249,6 +420,24 @@ const JSONBoolValue = struct {
|
||||
return self.boolean;
|
||||
}
|
||||
|
||||
fn print(value: *const JSONValue, writer: std.fs.File.Writer, _: JSONFormat, _: usize) std.fs.File.WriteError!void {
|
||||
const self: *const JSONBoolValue = @fieldParentPtr("value", value);
|
||||
if (self.boolean) {
|
||||
try writer.writeAll("true");
|
||||
} else {
|
||||
try writer.writeAll("false");
|
||||
}
|
||||
}
|
||||
|
||||
fn toString(value: *const JSONValue, allocator: std.mem.Allocator, _: JSONFormat, _: usize) std.mem.Allocator.Error![]const u8 {
|
||||
const self: *const JSONBoolValue = @fieldParentPtr("value", value);
|
||||
if (self.boolean) {
|
||||
return try allocator.dupe(u8, "true");
|
||||
} else {
|
||||
return try allocator.dupe(u8, "false");
|
||||
}
|
||||
}
|
||||
|
||||
fn deinit(value: *JSONValue) void {
|
||||
var self: *JSONBoolValue = @fieldParentPtr("value", value);
|
||||
self.value.allocator.destroy(self);
|
||||
@ -259,12 +448,12 @@ pub const JSONStringValue = struct {
|
||||
value: JSONValue,
|
||||
string: []const u8,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, string: []const u8) !*JSONValue {
|
||||
pub fn create(allocator: std.mem.Allocator, string: []const u8) !*JSONValue {
|
||||
var self = try allocator.create(JSONStringValue);
|
||||
self.string = string;
|
||||
self.value = .{
|
||||
.type = .String,
|
||||
.vtable = .{ .getString = getValue, .deinit = deinit },
|
||||
.vtable = .{ .getString = getValue, .print = print, .toString = toString, .deinit = deinit },
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
@ -276,6 +465,16 @@ pub const JSONStringValue = struct {
|
||||
return self.string;
|
||||
}
|
||||
|
||||
fn print(value: *const JSONValue, writer: std.fs.File.Writer, _: JSONFormat, _: usize) std.fs.File.WriteError!void {
|
||||
const self: *const JSONStringValue = @fieldParentPtr("value", value);
|
||||
try writer.print("\"{s}\"", .{self.string});
|
||||
}
|
||||
|
||||
fn toString(value: *const JSONValue, allocator: std.mem.Allocator, _: JSONFormat, _: usize) std.mem.Allocator.Error![]const u8 {
|
||||
const self: *const JSONStringValue = @fieldParentPtr("value", value);
|
||||
return std.fmt.allocPrint(allocator, "\"{s}\"", .{self.string});
|
||||
}
|
||||
|
||||
fn deinit(value: *JSONValue) void {
|
||||
var self: *JSONStringValue = @fieldParentPtr("value", value);
|
||||
self.value.allocator.free(self.string);
|
||||
@ -300,6 +499,14 @@ pub fn parseString(allocator: std.mem.Allocator, string: []const u8) !*JSONObjec
|
||||
return parser.parse();
|
||||
}
|
||||
|
||||
pub fn writeToFile(fileName: []const u8, json: *JSONObject, format: JSONFormat) !void {
|
||||
var file = try std.fs.cwd().createFile(fileName, .{});
|
||||
defer file.close();
|
||||
const writer = file.writer();
|
||||
|
||||
return json.print(writer, format, 0);
|
||||
}
|
||||
|
||||
const JSONParser = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
iter: std.unicode.Utf8Iterator,
|
||||
@ -354,7 +561,7 @@ const JSONParser = struct {
|
||||
}
|
||||
|
||||
fn parseObject(self: *JSONParser) JSONError!*JSONObject {
|
||||
const obj = try JSONObject.init(self.allocator);
|
||||
const obj = try JSONObject.create(self.allocator);
|
||||
errdefer obj.deinit();
|
||||
|
||||
try self.expectNext('{');
|
||||
@ -427,7 +634,7 @@ const JSONParser = struct {
|
||||
'"' => {
|
||||
//std.debug.print("Parsing string\n", .{});
|
||||
const str = try self.parseString();
|
||||
value = try JSONStringValue.init(self.allocator, str);
|
||||
value = try JSONStringValue.create(self.allocator, str);
|
||||
},
|
||||
'-', '0'...'9' => {
|
||||
//std.debug.print("Parsing number\n", .{});
|
||||
@ -436,17 +643,17 @@ const JSONParser = struct {
|
||||
'{' => {
|
||||
//std.debug.print("Parsing object\n", .{});
|
||||
const obj = try self.parseObject();
|
||||
value = try JSONObjectValue.init(self.allocator, obj);
|
||||
value = try JSONObjectValue.create(self.allocator, obj);
|
||||
},
|
||||
'[' => {
|
||||
//std.debug.print("Parsing array\n", .{});
|
||||
const arr = try self.parseArray();
|
||||
value = try JSONArrayValue.init(self.allocator, arr);
|
||||
value = try JSONArrayValue.create(self.allocator, arr);
|
||||
},
|
||||
't', 'f' => {
|
||||
//std.debug.print("Parsing bool\n", .{});
|
||||
const b = try self.parseBool();
|
||||
value = try JSONBoolValue.init(self.allocator, b);
|
||||
value = try JSONBoolValue.create(self.allocator, b);
|
||||
},
|
||||
'n' => {
|
||||
//std.debug.print("Parsing null\n", .{});
|
||||
@ -640,10 +847,10 @@ const JSONParser = struct {
|
||||
value *= std.math.pow(f64, 10.0, @floatFromInt(exp));
|
||||
}
|
||||
|
||||
return try JSONFloatValue.init(self.allocator, value);
|
||||
return try JSONFloatValue.create(self.allocator, value);
|
||||
} else {
|
||||
const value: isize = std.fmt.parseInt(isize, buf.items, 10) catch unreachable;
|
||||
return try JSONIntValue.init(self.allocator, value);
|
||||
return try JSONIntValue.create(self.allocator, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -892,3 +1099,8 @@ test "file parsing" {
|
||||
const root = try parseFile(std.testing.allocator, "test/test.json");
|
||||
root.deinit();
|
||||
}
|
||||
|
||||
test "123" {
|
||||
var root = try JSONObject.create(std.testing.allocator);
|
||||
defer root.deinit();
|
||||
}
|
||||
|
14
src/main.zig
14
src/main.zig
@ -8,12 +8,14 @@ pub fn main() void {
|
||||
}
|
||||
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const allocator = gpa.allocator();
|
||||
defer {
|
||||
if (gpa.deinit() == .leak) {
|
||||
std.debug.print("Memory was leaked D:\n", .{});
|
||||
}
|
||||
}
|
||||
var arena = std.heap.ArenaAllocator.init(gpa.allocator());
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
|
||||
const fileName = std.mem.span(std.os.argv[1]);
|
||||
|
||||
@ -21,10 +23,18 @@ pub fn main() void {
|
||||
std.debug.print("Failed to parse json: {any}\n", .{err});
|
||||
std.process.exit(2);
|
||||
};
|
||||
defer root.deinit();
|
||||
//defer root.deinit();
|
||||
|
||||
printObject(root, 0);
|
||||
std.debug.print("\n", .{});
|
||||
|
||||
const stdout = std.io.getStdOut();
|
||||
const writer = stdout.writer();
|
||||
root.print(writer, .{}, 0) catch {};
|
||||
std.debug.print("\n", .{});
|
||||
|
||||
const json_str = root.toString(allocator, .{}, 0) catch return;
|
||||
std.debug.print("{s}\n", .{json_str});
|
||||
}
|
||||
|
||||
fn printIndent(indent: usize) void {
|
||||
|
Loading…
Reference in New Issue
Block a user