Skip to content

Instantly share code, notes, and snippets.

@nickelca
Last active December 15, 2024 23:18
Show Gist options
  • Save nickelca/a203f2d08ceec8ef24384cfb474dfc8d to your computer and use it in GitHub Desktop.
Save nickelca/a203f2d08ceec8ef24384cfb474dfc8d to your computer and use it in GitHub Desktop.
Text components with std.json parsing
const Text_Object = @This();
list: []const Text,
pub const Text = union(enum) {
component: Component,
plain: []const u8,
pub fn jsonParse(
ally: std.mem.Allocator,
tokens: anytype,
options: std.json.ParseOptions,
) std.json.ParseError(@TypeOf(tokens.*))!Text {
return switch (try tokens.peekNextTokenType()) {
.string => .{
.plain = try std.json.innerParse([]const u8, ally, tokens, options),
},
.object_begin => .{
.component = try std.json.innerParse(Component, ally, tokens, options),
},
else => error.UnexpectedToken,
};
}
};
pub const Component = struct {
text: []const u8,
color: ?[]const u8 = null,
italic: bool = false,
bold: bool = false,
obfuscated: bool = false,
pub fn equals(self: Component, to: Component) bool {
return std.mem.eql(u8, self.text, to.text) and
self.italic == to.italic and
self.bold == to.bold and
self.obfuscated == to.obfuscated and
((self.color == null and to.color == null) or
(self.color != null and to.color != null and
std.mem.eql(u8, self.color.?, to.color.?)));
}
};
pub fn jsonParse(
ally: std.mem.Allocator,
tokens: anytype,
options: std.json.ParseOptions,
) std.json.ParseError(@TypeOf(tokens.*))!Text_Object {
const list: []Text = switch (try tokens.peekNextTokenType()) {
.array_begin => try std.json.innerParse([]Text, ally, tokens, options),
.string, .object_begin => try ally.dupe(Text, &.{
try std.json.innerParse(Text, ally, tokens, options),
}),
else => return error.UnexpectedToken,
};
return .{ .list = list };
}
fn Test_Text_Object_JSON(
arena: std.mem.Allocator,
expected: Text_Object,
json: []const u8,
) !void {
const obj = try std.json.parseFromSlice(Text_Object, arena, json, .{
.allocate = .alloc_if_needed,
});
defer obj.deinit();
try std.testing.expectEqual(expected.list.len, obj.value.list.len);
for (expected.list, obj.value.list) |e, a| {
switch (e) {
.plain => |t1| switch (a) {
.plain => |t2| try std.testing.expectEqualStrings(t1, t2),
.component => return error.TestExpectedEqual,
},
.component => |c1| switch (a) {
.plain => return error.TestExpectedEqual,
.component => |c2| try std.testing.expect(c1.equals(c2)),
},
}
}
}
test "Text Object JSON" {
var arena_state: std.heap.ArenaAllocator = .init(std.testing.allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
try Test_Text_Object_JSON(
arena,
.{ .list = &.{
.{ .plain = "Hello" },
} },
\\"Hello"
,
);
_ = arena_state.reset(.retain_capacity);
try Test_Text_Object_JSON(
arena,
.{ .list = &.{
.{ .component = .{ .text = "Apple" } },
} },
\\{"text": "Apple"}
,
);
_ = arena_state.reset(.retain_capacity);
try Test_Text_Object_JSON(
arena,
.{ .list = &.{
.{ .component = .{ .text = "Mansaf", .bold = true } },
} },
\\{"text": "Mansaf", "bold": true}
,
);
_ = arena_state.reset(.retain_capacity);
try Test_Text_Object_JSON(
arena,
.{ .list = &.{
.{ .component = .{ .text = "Woooo" } },
} },
\\[{"text": "Woooo"}]
,
);
_ = arena_state.reset(.retain_capacity);
try Test_Text_Object_JSON(
arena,
.{ .list = &.{
.{ .component = .{ .text = "Gaming" } },
.{ .plain = "std.json is goated" },
.{ .component = .{ .text = "W", .color = "red", .obfuscated = true } },
} },
\\[
\\ {"text": "Gaming"},
\\ "std.json is goated",
\\ {"text":"W", "color":"red", "obfuscated": true}
\\]
,
);
_ = arena_state.reset(.retain_capacity);
}
const std = @import("std");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment