Modularization

This commit is contained in:
Vivianne 2023-07-18 00:27:28 -07:00
parent 7747278b63
commit 2cb451daea
5 changed files with 428 additions and 420 deletions

View File

@ -1,8 +1,5 @@
const std = @import("std");
// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
@ -12,8 +9,6 @@ pub fn build(b: *std.Build) void {
const lib = b.addStaticLibrary(.{
.name = "zyrup",
// In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file.
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
@ -21,26 +16,33 @@ pub fn build(b: *std.Build) void {
lib.addModule("getty", getty_mod);
// This declares intent for the library to be installed into the standard
// location when the user invokes the "install" step (the default step when
// running `zig build`).
b.installArtifact(lib);
// Creates a step for unit testing. This only builds the test executable
// but does not run it.
const main_tests = b.addTest(.{
.root_source_file = .{ .path = "src/main.zig" },
const ser_tests = b.addTest(.{
.name = "serializer test",
.root_source_file = .{ .path = "src/ser.zig" },
.target = target,
.optimize = optimize,
});
main_tests.addModule("getty", getty_mod);
main_tests.addAnonymousModule("zoo.bin", .{ .source_file = std.Build.FileSource.relative("test-data/zoo.bin") });
ser_tests.addModule("getty", getty_mod);
ser_tests.addAnonymousModule("zoo.bin", .{ .source_file = std.Build.FileSource.relative("test-data/zoo.bin") });
const run_main_tests = b.addRunArtifact(main_tests);
const utf8_tests = b.addTest(.{
.name = "utf8 string test",
.root_source_file = .{ .path = "src/utf8_string.zig" },
.target = target,
.optimize = optimize,
});
const record_tests = b.addTest(.{
.name = "record test",
.root_source_file = .{ .path = "src/record.zig" },
.target = target,
.optimize = optimize,
});
// This creates a build step. It will be visible in the `zig build --help` menu,
// and can be selected like this: `zig build test`
// This will evaluate the `test` step rather than the default, which is "install".
const test_step = b.step("test", "Run library tests");
test_step.dependOn(&run_main_tests.step);
test_step.dependOn(&b.addRunArtifact(ser_tests).step);
test_step.dependOn(&b.addRunArtifact(utf8_tests).step);
test_step.dependOn(&b.addRunArtifact(record_tests).step);
}

View File

@ -1,401 +1,3 @@
const std = @import("std");
const testing = std.testing;
const getty = @import("getty");
const builtin = @import("builtin");
const native_endian = builtin.target.cpu.arch.endian();
const Utf8String = struct {
bytes: []const u8,
pub const Error = error{InvalidEncoding};
fn init(bytes: []const u8) Error!@This() {
if (!std.unicode.utf8ValidateSlice(bytes)) {
return error.InvalidEncoding;
}
return initAssumeEncoding(bytes);
}
fn initAssumeEncoding(bytes: []const u8) @This() {
return .{
.bytes = bytes,
};
}
pub const @"getty.sb" = struct {
pub fn serialize(
_: ?std.mem.Allocator,
value: anytype,
serializer: anytype,
) @TypeOf(serializer).Error!@TypeOf(serializer).Ok {
if (@TypeOf(serializer.context) == Serializer and @TypeOf(value) == Utf8String) {
try Serializer.serializeGenericString("\"", serializer.context.writer, value.bytes);
}
}
};
};
test "test invalid utf8" {
try testing.expectError(Utf8String.Error.InvalidEncoding, Utf8String.init("abc\xc0"));
}
pub fn Record(comptime label: anytype, comptime tuple: anytype) type {
const T = @TypeOf(tuple);
if (!std.meta.trait.isTuple(T)) {
@compileError("Type passed into Record constructor must be a tuple.");
}
return struct {
label: @TypeOf(label) = label,
values: @TypeOf(tuple) = tuple,
const Self = @This();
fn init() Self {
return Self{};
}
pub const @"getty.sb" = struct {
pub fn serialize(
allocator: ?std.mem.Allocator,
value: anytype,
serializer: anytype,
) @TypeOf(serializer).Error!@TypeOf(serializer).Ok {
if (@TypeOf(serializer.context) == Serializer and @TypeOf(value) == Self) {
try serializer.context.writer.writeByte('<');
try getty.serialize(allocator, value.label, serializer);
inline for (value.values) |val| {
try getty.serialize(allocator, val, serializer);
}
try serializer.context.writer.writeByte('>');
}
}
};
};
}
const Serializer = struct {
usingnamespace getty.Serializer(
Self,
Ok,
Error,
null,
null,
null,
Seq,
Struct,
.{
.serializeBool = serializeBool,
.serializeInt = serializeInt,
.serializeFloat = serializeFloat,
.serializeString = serializeString,
.serializeEnum = serializeEnum,
.serializeStruct = serializeStruct,
.serializeSeq = serializeSeq,
},
);
writer: std.ArrayList(u8).Writer,
const Self = @This();
const Ok = void;
const Error = getty.ser.Error || std.mem.Allocator.Error;
pub fn init(writer: std.ArrayList(u8).Writer) Self {
return Self{
.writer = writer,
};
}
pub fn serializeGenericString(comptime joiner: []const u8, writer: std.ArrayList(u8).Writer, value: anytype) Error!Ok {
try writer.print("{d}" ++ joiner, .{value.len});
try writer.print("{s}", .{value});
}
fn serializeBool(self: Self, value: bool) Error!Ok {
try self.writer.writeByte(if (value) 't' else 'f');
}
fn serializeInt(self: Self, value: anytype) Error!Ok {
try self.writer.print("{d}", .{std.math.absCast(value)});
try self.writer.writeByte(if (value >= 0) '+' else '-');
}
fn serializeFloat(self: Self, value: anytype) Error!Ok {
switch (@TypeOf(value)) {
f32 => {
try self.writer.writeByte('F');
try self.writer.writeInt(u32, @as(u32, @bitCast(value)), .Big);
},
f64 => {
try self.writer.writeByte('D');
try self.writer.writeInt(u64, @as(u64, @bitCast(value)), .Big);
},
else => @compileError("Can't serialize a float that isn't f32 or f64"),
}
}
fn serializeString(self: Self, value: anytype) Error!Ok {
try serializeGenericString(":", self.writer, value);
}
fn serializeEnum(self: Self, _: anytype, name: []const u8) Error!Ok {
try serializeGenericString("\'", self.writer, name);
}
fn serializeSeq(self: Self, _: ?usize) Error!Seq {
try self.writer.writeByte('[');
return Seq{ .writer = self.writer };
}
fn serializeStruct(self: Self, comptime _: []const u8, _: usize) Error!Struct {
try self.writer.writeByte('{');
return Struct{ .writer = self.writer };
}
};
const Seq = struct {
pub usingnamespace getty.ser.Seq(Self, Ok, Error, .{
.serializeElement = serializeElement,
.end = end,
});
writer: std.ArrayList(u8).Writer,
const Self = @This();
const Ok = Serializer.Ok;
const Error = Serializer.Error;
fn serializeElement(self: Self, value: anytype) Error!void {
try getty.serialize(null, value, Serializer.init(self.writer).serializer());
}
fn end(self: Self) Error!Ok {
try self.writer.writeByte(']');
}
};
const Struct = struct {
pub usingnamespace getty.ser.Structure(
Self,
Ok,
Error,
.{
.serializeField = serializeField,
.end = end,
},
);
writer: std.ArrayList(u8).Writer,
const Self = @This();
const Ok = Serializer.Ok;
const Error = Serializer.Error;
fn serializeField(self: Self, comptime field: []const u8, value: anytype) Error!void {
try Serializer.serializeGenericString("'", self.writer, field);
try getty.serialize(null, value, Serializer.init(self.writer).serializer());
}
fn end(self: Self) Error!Ok {
try self.writer.writeByte('}');
}
};
test "serialize bool" {
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, true, s);
try getty.serialize(null, false, s);
try getty.serialize(null, true, s);
try testing.expectEqualStrings("tft", arr.items);
}
test "serialize ints" {
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, 42, s);
try getty.serialize(null, 86, s);
try getty.serialize(null, -356, s);
try testing.expectEqualStrings("42+86+356-", arr.items);
}
test "serialize floats" {
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, @as(f64, 42.42), s);
try getty.serialize(null, @as(f32, 42.42), s);
try testing.expectEqualStrings(&[_]u8{ 'D', 64, 69, 53, 194, 143, 92, 40, 246, 'F', 66, 41, 174, 20 }, arr.items);
}
test "serialize bytestrs" {
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, "hello", s);
try getty.serialize(null, "my name is vivi 💖", s);
try testing.expectEqualStrings("5:hello20:my name is vivi 💖", arr.items);
}
test "serialize strs" {
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, try Utf8String.init("hello"), s);
try getty.serialize(null, try Utf8String.init("my name is vivi 💖"), s);
try testing.expectEqualStrings("5\"hello20\"my name is vivi 💖", arr.items);
}
test "serialize enum, aka symbol" {
const Testing = enum {
cool,
bad,
shitty,
};
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, Testing.cool, s);
try testing.expectEqualStrings("4'cool", arr.items);
}
test "serialize struct" {
const Testing = struct {
val1: u8,
val2: bool,
val3: []const u8,
};
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, Testing{
.val1 = 25,
.val2 = false,
.val3 = "hi there",
}, s);
try testing.expectEqualStrings("{4'val125+4'val2f4'val38:hi there}", arr.items);
}
test "serialize mixed" {
const Testing = enum {
a,
testing,
enumeration,
};
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, 42, s);
try getty.serialize(null, "my name is vivi 💖", s);
try getty.serialize(null, true, s);
try getty.serialize(null, Testing.enumeration, s);
try getty.serialize(null, @as(f32, 69.420), s);
try testing.expectEqualStrings("42+20:my name is vivi 💖t11'enumerationF" ++ [_]u8{ 66, 138, 215, 10 }, arr.items);
}
test "serialize record" {
// A record is generated when a tuple starts with []const u8, the label.
const rec = Record("label", .{
@as(u32, 42),
@as(f64, 24.42),
"Hello there",
}).init();
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, rec, s);
try testing.expectEqualStrings("<5:label42+D" ++ [_]u8{ 64, 56, 107, 133, 30, 184, 81, 236 } ++ "11:Hello there>", arr.items);
}
// test "zoo" {
// const zooFile = @embedFile("zoo.bin");
// const Animal = struct {
// species: []const u8,
// name: Utf8String,
// age: i32,
// weight: f64,
// @"alive?": bool,
// eats: std.StringArrayHashMap(void),
// };
// var arr = std.ArrayList(u8).init(std.testing.allocator);
// defer arr.deinit();
// var s = Serializer.init(arr.writer()).serializer();
// var mapCat = std.StringArrayHashMap(void).init(std.testing.allocator);
// defer mapCat.deinit();
// try mapCat.put("fish", {});
// try mapCat.put("mice", {});
// try mapCat.put("kibble", {});
// var mapMonkey = std.StringArrayHashMap(void).init(std.testing.allocator);
// defer mapMonkey.deinit();
// try mapMonkey.put("insects", {});
// try mapMonkey.put("bananas", {});
// var mapGhost = std.StringArrayHashMap(void).init(std.testing.allocator);
// defer mapGhost.deinit();
// const zoo = .{
// Utf8String.initAssumeEncoding("The Grand Menagerie"),
// [_]Animal{
// .{
// .species = "cat",
// .name = Utf8String.initAssumeEncoding("Tabatha"),
// .age = 12,
// .weight = 8.2,
// .@"alive?" = true,
// .eats = mapCat,
// },
// .{
// .species = "monkey",
// .name = Utf8String.initAssumeEncoding("George"),
// .age = 6,
// .weight = 17.24,
// .@"alive?" = false,
// .eats = mapMonkey,
// },
// .{
// .species = "ghost",
// .name = Utf8String.initAssumeEncoding("Casper"),
// .age = -12,
// .weight = -34.5,
// .@"alive?" = false,
// .eats = mapGhost,
// },
// },
// };
// try getty.serialize(null, zoo, s);
// try testing.expectEqualStrings(zooFile, arr.items);
// }
pub const Record = @import("record.zig");
pub const Utf8String = @import("utf8_string.zig");
pub const Serializer = @import("ser.zig");

43
src/record.zig Normal file
View File

@ -0,0 +1,43 @@
const std = @import("std");
const getty = @import("getty");
const Serializer = @import("ser.zig");
pub fn Record(comptime label: anytype, comptime tuple: anytype) type {
const T = @TypeOf(tuple);
if (!std.meta.trait.isTuple(T)) {
@compileError("Type passed into Record constructor must be a tuple.");
}
return struct {
label: @TypeOf(label) = label,
values: @TypeOf(tuple) = tuple,
const Self = @This();
pub fn init() Self {
return Self{};
}
pub const @"getty.sb" = struct {
pub fn serialize(
allocator: ?std.mem.Allocator,
value: anytype,
serializer: anytype,
) @TypeOf(serializer).Error!@TypeOf(serializer).Ok {
if (@TypeOf(serializer.context) == Serializer and @TypeOf(value) == Self) {
try serializer.context.writer.writeByte('<');
try getty.serialize(allocator, value.label, serializer);
inline for (value.values) |val| {
try getty.serialize(allocator, val, serializer);
}
try serializer.context.writer.writeByte('>');
}
}
};
};
}

322
src/ser.zig Normal file
View File

@ -0,0 +1,322 @@
const std = @import("std");
const testing = std.testing;
const getty = @import("getty");
const Record = @import("record.zig").Record;
const Utf8String = @import("utf8_string.zig");
const Serializer = @This();
usingnamespace getty.Serializer(
Serializer,
Ok,
Error,
null,
null,
null,
Seq,
Struct,
.{
.serializeBool = serializeBool,
.serializeInt = serializeInt,
.serializeFloat = serializeFloat,
.serializeString = serializeString,
.serializeEnum = serializeEnum,
.serializeStruct = serializeStruct,
.serializeSeq = serializeSeq,
},
);
writer: std.ArrayList(u8).Writer,
const Ok = void;
const Error = getty.ser.Error || std.mem.Allocator.Error;
pub fn init(writer: std.ArrayList(u8).Writer) Serializer {
return Serializer{
.writer = writer,
};
}
pub fn serializeGenericString(comptime joiner: []const u8, writer: std.ArrayList(u8).Writer, value: anytype) Error!Ok {
try writer.print("{d}" ++ joiner, .{value.len});
try writer.print("{s}", .{value});
}
fn serializeBool(self: Serializer, value: bool) Error!Ok {
try self.writer.writeByte(if (value) 't' else 'f');
}
fn serializeInt(self: Serializer, value: anytype) Error!Ok {
try self.writer.print("{d}", .{std.math.absCast(value)});
try self.writer.writeByte(if (value >= 0) '+' else '-');
}
fn serializeFloat(self: Serializer, value: anytype) Error!Ok {
switch (@TypeOf(value)) {
f32 => {
try self.writer.writeByte('F');
try self.writer.writeInt(u32, @as(u32, @bitCast(value)), .Big);
},
f64 => {
try self.writer.writeByte('D');
try self.writer.writeInt(u64, @as(u64, @bitCast(value)), .Big);
},
else => @compileError("Can't serialize a float that isn't f32 or f64"),
}
}
fn serializeString(self: Serializer, value: anytype) Error!Ok {
try serializeGenericString(":", self.writer, value);
}
fn serializeEnum(self: Serializer, _: anytype, name: []const u8) Error!Ok {
try serializeGenericString("\'", self.writer, name);
}
fn serializeSeq(self: Serializer, _: ?usize) Error!Seq {
try self.writer.writeByte('[');
return Seq{ .writer = self.writer };
}
fn serializeStruct(self: Serializer, comptime _: []const u8, _: usize) Error!Struct {
try self.writer.writeByte('{');
return Struct{ .writer = self.writer };
}
const Seq = struct {
pub usingnamespace getty.ser.Seq(Self, Ok, Error, .{
.serializeElement = serializeElement,
.end = end,
});
writer: std.ArrayList(u8).Writer,
const Self = @This();
fn serializeElement(self: Self, value: anytype) Error!void {
try getty.serialize(null, value, Serializer.init(self.writer).serializer());
}
fn end(self: Self) Error!Ok {
try self.writer.writeByte(']');
}
};
const Struct = struct {
pub usingnamespace getty.ser.Structure(
Self,
Ok,
Error,
.{
.serializeField = serializeField,
.end = end,
},
);
writer: std.ArrayList(u8).Writer,
const Self = @This();
fn serializeField(self: Self, comptime field: []const u8, value: anytype) Error!void {
try Serializer.serializeGenericString("'", self.writer, field);
try getty.serialize(null, value, Serializer.init(self.writer).serializer());
}
fn end(self: Self) Error!Ok {
try self.writer.writeByte('}');
}
};
test "serialize bool" {
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, true, s);
try getty.serialize(null, false, s);
try getty.serialize(null, true, s);
try testing.expectEqualStrings("tft", arr.items);
}
test "serialize ints" {
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, 42, s);
try getty.serialize(null, 86, s);
try getty.serialize(null, -356, s);
try testing.expectEqualStrings("42+86+356-", arr.items);
}
test "serialize floats" {
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, @as(f64, 42.42), s);
try getty.serialize(null, @as(f32, 42.42), s);
try testing.expectEqualStrings(&[_]u8{ 'D', 64, 69, 53, 194, 143, 92, 40, 246, 'F', 66, 41, 174, 20 }, arr.items);
}
test "serialize bytestrs" {
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, "hello", s);
try getty.serialize(null, "my name is vivi 💖", s);
try testing.expectEqualStrings("5:hello20:my name is vivi 💖", arr.items);
}
test "serialize strs" {
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, try Utf8String.init("hello"), s);
try getty.serialize(null, try Utf8String.init("my name is vivi 💖"), s);
try testing.expectEqualStrings("5\"hello20\"my name is vivi 💖", arr.items);
}
test "serialize enum, aka symbol" {
const Testing = enum {
cool,
bad,
shitty,
};
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, Testing.cool, s);
try testing.expectEqualStrings("4'cool", arr.items);
}
test "serialize struct" {
const Testing = struct {
val1: u8,
val2: bool,
val3: []const u8,
};
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, Testing{
.val1 = 25,
.val2 = false,
.val3 = "hi there",
}, s);
try testing.expectEqualStrings("{4'val125+4'val2f4'val38:hi there}", arr.items);
}
test "serialize mixed" {
const Testing = enum {
a,
testing,
enumeration,
};
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, 42, s);
try getty.serialize(null, "my name is vivi 💖", s);
try getty.serialize(null, true, s);
try getty.serialize(null, Testing.enumeration, s);
try getty.serialize(null, @as(f32, 69.420), s);
try testing.expectEqualStrings("42+20:my name is vivi 💖t11'enumerationF" ++ [_]u8{ 66, 138, 215, 10 }, arr.items);
}
test "serialize record" {
// A record is generated when a tuple starts with []const u8, the label.
const rec = Record("label", .{
@as(u32, 42),
@as(f64, 24.42),
"Hello there",
}).init();
var arr = std.ArrayList(u8).init(std.testing.allocator);
defer arr.deinit();
var s = Serializer.init(arr.writer()).serializer();
try getty.serialize(null, rec, s);
try testing.expectEqualStrings("<5:label42+D" ++ [_]u8{ 64, 56, 107, 133, 30, 184, 81, 236 } ++ "11:Hello there>", arr.items);
}
// test "zoo" {
// const zooFile = @embedFile("zoo.bin");
// const Animal = struct {
// species: []const u8,
// name: Utf8String,
// age: i32,
// weight: f64,
// @"alive?": bool,
// eats: std.StringArrayHashMap(void),
// };
// var arr = std.ArrayList(u8).init(std.testing.allocator);
// defer arr.deinit();
// var s = Serializer.init(arr.writer()).serializer();
// var mapCat = std.StringArrayHashMap(void).init(std.testing.allocator);
// defer mapCat.deinit();
// try mapCat.put("fish", {});
// try mapCat.put("mice", {});
// try mapCat.put("kibble", {});
// var mapMonkey = std.StringArrayHashMap(void).init(std.testing.allocator);
// defer mapMonkey.deinit();
// try mapMonkey.put("insects", {});
// try mapMonkey.put("bananas", {});
// var mapGhost = std.StringArrayHashMap(void).init(std.testing.allocator);
// defer mapGhost.deinit();
// const zoo = .{
// Utf8String.initAssumeEncoding("The Grand Menagerie"),
// [_]Animal{
// .{
// .species = "cat",
// .name = Utf8String.initAssumeEncoding("Tabatha"),
// .age = 12,
// .weight = 8.2,
// .@"alive?" = true,
// .eats = mapCat,
// },
// .{
// .species = "monkey",
// .name = Utf8String.initAssumeEncoding("George"),
// .age = 6,
// .weight = 17.24,
// .@"alive?" = false,
// .eats = mapMonkey,
// },
// .{
// .species = "ghost",
// .name = Utf8String.initAssumeEncoding("Casper"),
// .age = -12,
// .weight = -34.5,
// .@"alive?" = false,
// .eats = mapGhost,
// },
// },
// };
// try getty.serialize(null, zoo, s);
// try testing.expectEqualStrings(zooFile, arr.items);
// }

39
src/utf8_string.zig Normal file
View File

@ -0,0 +1,39 @@
const std = @import("std");
const testing = std.testing;
const Serializer = @import("ser.zig");
const Self = @This();
bytes: []const u8,
pub const Error = error{InvalidEncoding};
pub fn init(bytes: []const u8) Error!@This() {
if (!std.unicode.utf8ValidateSlice(bytes)) {
return error.InvalidEncoding;
}
return initAssumeEncoding(bytes);
}
pub fn initAssumeEncoding(bytes: []const u8) @This() {
return .{
.bytes = bytes,
};
}
pub const @"getty.sb" = struct {
pub fn serialize(
_: ?std.mem.Allocator,
value: anytype,
serializer: anytype,
) @TypeOf(serializer).Error!@TypeOf(serializer).Ok {
if (@TypeOf(serializer.context) == Serializer and @TypeOf(value) == Self) {
try Serializer.serializeGenericString("\"", serializer.context.writer, value.bytes);
}
}
};
test "test invalid utf8" {
try testing.expectError(Self.Error.InvalidEncoding, Self.init("abc\xc0"));
}