Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added JSON stringify function to Table #17

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,30 @@ This library is self-contained and requires no dependencies.
To build simply run `zig build` and that will output a file called `libtoml.a` that you can link with your program.

If you want to run the tests then use the `zig build test` command.

## Installation

Add this to you build.zig
```zig
const zigtoml = b.dependency("zigtoml", .{
.target = target,
.optimize = optimize,
});
exe.addModule("toml", zigtoml.module("toml"));
```

Add this to your build.zig.zon

```zig
.zigtoml = .{
.url = "https://github.com/aeronavery/zig-toml/archive/refs/heads/master.tar.gz",
//hash will be suggested by the zig compiler
}
```

This can then be imported into your code like this

```zig
const toml = @import("toml");

```
5 changes: 5 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const Builder = @import("std").build.Builder;
const std = @import("std");

pub fn build(b: *Builder) void {
const target = b.standardTargetOptions(.{});
Expand All @@ -10,6 +11,10 @@ pub fn build(b: *Builder) void {
.target = target,
});
b.installArtifact(lib);
const module = b.addModule("toml", std.Build.CreateModuleOptions{
.source_file = .{ .path = "src/toml.zig" },
});
lib.addModule("toml", module);

const main_tests = b.addTest(.{
.root_source_file = .{ .path = "src/toml.zig" },
Expand Down
191 changes: 191 additions & 0 deletions src/toml.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ pub const Key = union(enum) {
pub const DynamicArray = std.ArrayList(Value);
pub const TableArray = std.ArrayList(*Table);

pub const TomlStringifyError = error{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of the redefinition of all of the error values here. I could be wrong but it seems the only possible error with stringify is the OutOfMemory error value, so a simple !std.ArrayList(u8) should suffice.

table_is_one,
key_already_exists,
expected_table_of_one,
OutOfMemory,
};

pub const Value = union(enum) {
None,
String: []const u8,
Expand Down Expand Up @@ -60,6 +67,77 @@ pub const Value = union(enum) {
else => {},
}
}

pub fn stringify(self: Value, a: std.mem.Allocator) TomlStringifyError!std.ArrayList(u8) {
var result = std.ArrayList(u8).init(a);
errdefer result.deinit();

switch (self) {
.None => {},
.String => {
try result.append('\"');
try result.appendSlice(self.String);
try result.append('\"');
},
.Boolean => {
switch (self.Boolean) {
true => try result.appendSlice("true"),
false => try result.appendSlice("false"),
}
},
.Integer => {
var stringified = try std.fmt.allocPrint(a, "{}", .{self.Integer});
errdefer a.free(stringified);
try result.appendSlice(stringified);
a.free(stringified);
},
.Float => {
var stringified = try std.fmt.allocPrint(a, "{}", .{self.Float});
errdefer a.free(stringified);
try result.appendSlice(stringified);
a.free(stringified);
},
.Array => {
try result.append('[');
var first_element = true;
for (self.Array.items) |val| {

//skip comma on first element
if (!first_element) {
try result.append(',');
}
first_element = false;
var val_string = try val.stringify(a);
try result.appendSlice(val_string.items);
val_string.deinit();
}
try result.append(']');
},
.Table => {
var table_string = try self.Table.stringify();
try result.appendSlice(table_string.items);
table_string.deinit();
},
.ManyTables => {
try result.append('[');
var first_element = true;
for (self.ManyTables.items) |table| {

//skip comma on first element
if (!first_element) {
try result.append(',');
}
first_element = false;
var table_string = try table.stringify();
try result.appendSlice(table_string.items);
table_string.deinit();
}

try result.append(']');
},
}
return result;
}
};

pub const Table = struct {
Expand Down Expand Up @@ -184,6 +262,43 @@ pub const Table = struct {
}
return table;
}

///iterates over all the keys in the Table
pub inline fn iterator(self: *Self) KeyMap.Iterator {
return self.keys.iterator();
}

///emits json
pub fn stringify(self: *@This()) TomlStringifyError!std.ArrayList(u8) {
var result = std.ArrayList(u8).init(self.allocator);
errdefer result.deinit();

try result.append('{');

var first_iteration = true;
var iter = self.iterator();
while (iter.next()) |elem| {

//skip leading comma on first iteration
if (!first_iteration) {
try result.append(',');
}
first_iteration = false;

try result.append('\"');
try result.appendSlice(elem.key_ptr.*);
try result.appendSlice("\":");

const value_string = try elem.value_ptr.stringify(self.allocator);
errdefer value_string.deinit();
try result.appendSlice(value_string.items);
value_string.deinit();
}

try result.append('}');

return result;
}
};

fn isEof(c: u8) bool {
Expand Down Expand Up @@ -1498,3 +1613,79 @@ test "inline table with inline table" {
var foobar = bar.keys.get("foobar").?.String;
assert(std.mem.eql(u8, foobar, "test string"));
}

test "stringify" {
var parser = try parseContents(std.testing.allocator,
\\ foo="hello"
\\ bar=false
\\ bizz="bazz"
\\ new=1000
\\ bip=12.24
);
defer parser.deinit();

var table = try parser.parse();
defer table.deinit();

var json = try table.stringify();
defer json.deinit();

try std.testing.expect(std.mem.eql(u8, json.items,
\\{"bizz":"bazz","bip":1.224e+01,"bar":false,"new":1000,"foo":"hello"}
));
}

test "stringify_tables" {
var parser = try parseContents(std.testing.allocator,
\\ [bazz]
\\ foo="hello"
\\ bar=false
\\
\\ [mizz]
\\ carp=true
);
defer parser.deinit();

var table = try parser.parse();
defer table.deinit();

var json = try table.stringify();
defer json.deinit();

try std.testing.expect(std.mem.eql(u8, json.items,
\\{"bazz":{"bar":false,"foo":"hello"},"mizz":{"carp":true}}
));
}

test "parsing toml into type" {
const toml =
\\[data]
\\author = "Robert"
\\github = "VisenDev"
\\#heres a comment
\\
\\[numbers]
\\list = [1, 2, 3]
;
const toml_type = struct {
data: struct {
author: []const u8,
github: []const u8,
},
numbers: struct {
list: []const u32,
},
};

var parser = try parseContents(std.testing.allocator, toml);
defer parser.deinit();

var table = try parser.parse();
defer table.deinit();

var json = try table.stringify();
defer json.deinit();

const parsed = try std.json.parseFromSlice(toml_type, std.testing.allocator, json.items, .{});
defer parsed.deinit();
}