~ntgg/serdes-zig

e40f5f7bfd808ab4a689054ccce84417ebfe7670 — Noah Graff 6 months ago 5fd122a
added value -> text conversion

Currently is just `std.json.Value.dump`, but with `std.debug.warn`
replaced with `std.io.OutStream.print`. The names are real messy and
the args could do with improvements, but it is usable right now.

I also opened https://github.com/ziglang/zig/issues/3008, and if it is
accepted this will be moved to the standard library.
1 files changed, 173 insertions(+), 0 deletions(-)

M src/main.zig
M src/main.zig => src/main.zig +173 -0
@@ 1,4 1,177 @@
const std = @import("std");
const OutStream = std.io.OutStream;

pub const serialize = @import("serialize.zig").serialize;
pub const serializeValue = @import("serialize.zig").serializeValue;
// can't do on current master
pub const deserialize = @import("deserialize.zig").deserialize;

pub fn valueToJson(comptime WriteError: type, out_stream: *OutStream(WriteError), value: std.json.Value) WriteError!void {
    switch (value) {
        .Null => {
            try out_stream.print("null");
        },
        .Bool => |inner| {
            try out_stream.print("{}", inner);
        },
        .Integer => |inner| {
            try out_stream.print("{}", inner);
        },
        .Float => |inner| {
            try out_stream.print("{:.5}", inner);
        },
        .String => |inner| {
            try out_stream.print("\"{}\"", inner);
        },
        .Array => |inner| {
            var not_first = false;
            try out_stream.print("[");
            for (inner.toSliceConst()) |item| {
                if (not_first) {
                    try out_stream.print(",");
                }
                not_first = true;
                try valueToJson(WriteError, out_stream, value);
            }
            try out_stream.print("]");
        },
        .Object => |inner| {
            var not_first = false;
            try out_stream.print("{{");
            var it = inner.iterator();

            while (it.next()) |entry| {
                if (not_first) {
                    try out_stream.print(",");
                }
                not_first = true;
                try out_stream.print("\"{}\":", entry.key);
                try valueToJson(WriteError, out_stream, entry.value);
            }
            try out_stream.print("}}");
        },
    }
}

pub fn valueToJsonIndent(
    comptime WriteError: type,
    out_stream: *OutStream(WriteError),
    value: std.json.Value,
    indent: usize,
) WriteError!void {
    if (indent == 0) {
        try valueToJson(WriteError, out_stream, value);
    } else {
        try valueToJsonIndentLevel(WriteError, out_stream, value, indent, 0);
    }
}

fn valueToJsonIndentLevel(
    comptime WriteError: type,
    out_stream: *OutStream(WriteError),
    value: std.json.Value,
    indent: usize,
    level: usize,
) WriteError!void {
    switch (value) {
        .Null => {
            try out_stream.print("null");
        },
        .Bool => |inner| {
            try out_stream.print("{}", inner);
        },
        .Integer => |inner| {
            try out_stream.print("{}", inner);
        },
        .Float => |inner| {
            try out_stream.print("{:.5}", inner);
        },
        .String => |inner| {
            try out_stream.print("\"{}\"", inner);
        },
        .Array => |inner| {
            var not_first = false;
            try out_stream.print("[\n");

            for (inner.toSliceConst()) |item| {
                if (not_first) {
                    try out_stream.print(",\n");
                }
                not_first = true;
                try padSpace(WriteError, out_stream, level + indent);
                try valueToJsonIndentLevel(WriteError, out_stream, item, indent, level + indent);
            }
            try out_stream.print("\n");
            try padSpace(WriteError, out_stream, level);
            try out_stream.print("]");
        },
        .Object => |inner| {
            var not_first = false;
            try out_stream.print("{{\n");
            var it = inner.iterator();

            while (it.next()) |entry| {
                if (not_first) {
                    try out_stream.print(",\n");
                }
                not_first = true;
                try padSpace(WriteError, out_stream, level + indent);
                try out_stream.print("\"{}\": ", entry.key);
                try valueToJsonIndentLevel(WriteError, out_stream, entry.value, indent, level + indent);
            }
            try out_stream.print("\n");
            try padSpace(WriteError, out_stream, level);
            try out_stream.print("}}");
        },
    }
}

fn padSpace(comptime WriteError: type, out_stream: *OutStream(WriteError), spaces: usize) WriteError!void {
    var i: usize = 0;
    while (i < spaces) : (i += 1) try out_stream.print(" ");
}

test "valueToJsonIndent" {
    const t = std.testing;

    const Person = struct {
        first_name: []const u8,
        last_name: []const u8,
        age: u8,
        colors: [3]u24,
    };

    var value_tree = try serialize(
        std.debug.global_allocator,
        Person,
        Person{
            .first_name = "Noah",
            .last_name = "Graff",
            .age = 19,
            .colors = [_]u24{ 0xDDA0DD, 0xD38D5D, 0xAFEEEE },
        },
    );
    defer value_tree.deinit();

    var buffer = try std.Buffer.init(std.debug.global_allocator, "");
    defer buffer.deinit();
    var buffer_stream = std.io.BufferOutStream.init(&buffer);

    try valueToJsonIndent(std.io.BufferOutStream.Error, &buffer_stream.stream, value_tree.root, 4);
    // std.debug.warn("\n{}\n", buffer.toSliceConst());
    t.expect(std.json.validate(buffer.toSliceConst()));
    t.expectEqualSlices(
        u8,
        buffer.toSliceConst(),
        \\{
        \\    "first_name": "Noah",
        \\    "colors": [
        \\        14524637,
        \\        13864285,
        \\        11529966
        \\    ],
        \\    "last_name": "Graff",
        \\    "age": 19
        \\}
    );
}