Compare commits
No commits in common. "1d65984323e5da02c8aa1dcb5bd385868e54cb33" and "0930eee5610d45823178b041620939da176d6874" have entirely different histories.
1d65984323
...
0930eee561
5 changed files with 100 additions and 122 deletions
|
@ -163,12 +163,27 @@ test "InjectContextValue" {
|
||||||
.handle(.{}, .{}, .{ .efgh = @as(usize, 10) }, ExpectContext(.{ .abcd = 5, .efgh = 10 }){});
|
.handle(.{}, .{}, .{ .efgh = @as(usize, 10) }, ExpectContext(.{ .abcd = 5, .efgh = 10 }){});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expectDeepEquals(expected: anytype, actual: anytype) !void {
|
||||||
|
const E = @TypeOf(expected);
|
||||||
|
const A = @TypeOf(actual);
|
||||||
|
if (E == void) return std.testing.expect(A == void);
|
||||||
|
try std.testing.expect(std.meta.fields(E).len == std.meta.fields(A).len);
|
||||||
|
inline for (std.meta.fields(E)) |f| {
|
||||||
|
const e = @field(expected, f.name);
|
||||||
|
const a = @field(actual, f.name);
|
||||||
|
if (comptime std.meta.trait.isZigString(f.field_type)) {
|
||||||
|
try std.testing.expectEqualStrings(a, e);
|
||||||
|
} else {
|
||||||
|
try std.testing.expectEqual(a, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Helper for testing purposes
|
// Helper for testing purposes
|
||||||
fn ExpectContext(comptime val: anytype) type {
|
fn ExpectContext(comptime val: anytype) type {
|
||||||
return struct {
|
return struct {
|
||||||
pub fn handle(_: @This(), _: anytype, _: anytype, ctx: anytype, _: void) !void {
|
pub fn handle(_: @This(), _: anytype, _: anytype, ctx: anytype, _: void) !void {
|
||||||
if (@TypeOf(val) == void) return error.TestUnexpectedResult;
|
try expectDeepEquals(val, ctx);
|
||||||
try util.testing.expectDeepEqual(@as(@TypeOf(ctx), val), ctx);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -470,15 +485,7 @@ pub fn mount(comptime route: []const u8) Mount(route) {
|
||||||
test "mount" {
|
test "mount" {
|
||||||
const testCase = struct {
|
const testCase = struct {
|
||||||
fn func(comptime base: []const u8, request: []const u8, comptime expected: ?[]const u8) !void {
|
fn func(comptime base: []const u8, request: []const u8, comptime expected: ?[]const u8) !void {
|
||||||
const result = mount(base).handle(
|
const result = mount(base).handle(.{}, .{}, addField(.{}, "path", request), expectContext(.{ .path = expected orelse "" }));
|
||||||
.{},
|
|
||||||
.{},
|
|
||||||
addField(.{}, "path", request),
|
|
||||||
expectContext(.{
|
|
||||||
.path = expected orelse "",
|
|
||||||
.mounted_at = std.mem.trim(u8, base, "/"),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
try if (expected != null) result else std.testing.expectError(error.RouteMismatch, result);
|
try if (expected != null) result else std.testing.expectError(error.RouteMismatch, result);
|
||||||
}
|
}
|
||||||
}.func;
|
}.func;
|
||||||
|
@ -583,8 +590,7 @@ test "ParsePathArgs" {
|
||||||
expected: @TypeOf(expected),
|
expected: @TypeOf(expected),
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
fn handle(self: @This(), _: anytype, _: anytype, ctx: anytype, _: void) !void {
|
fn handle(self: @This(), _: anytype, _: anytype, ctx: anytype, _: void) !void {
|
||||||
if (@TypeOf(expected) == @TypeOf(null)) return error.TestUnexpectedResult;
|
try expectDeepEquals(self.expected, ctx.args);
|
||||||
try util.testing.expectDeepEqual(@as(@TypeOf(ctx.args), self.expected), ctx.args);
|
|
||||||
try std.testing.expectEqualStrings(self.path, ctx.path);
|
try std.testing.expectEqualStrings(self.path, ctx.path);
|
||||||
}
|
}
|
||||||
}{ .expected = expected, .path = path };
|
}{ .expected = expected, .path = path };
|
||||||
|
@ -613,10 +619,10 @@ test "ParsePathArgs" {
|
||||||
try testCase("/:foo*", struct { foo: []const u8 }, "/", .{ .foo = "/" });
|
try testCase("/:foo*", struct { foo: []const u8 }, "/", .{ .foo = "/" });
|
||||||
try testCase("/:foo*", struct { foo: []const u8 }, "", .{ .foo = "" });
|
try testCase("/:foo*", struct { foo: []const u8 }, "", .{ .foo = "" });
|
||||||
|
|
||||||
try std.testing.expectError(error.RouteMismatch, testCase("/:id", struct { id: usize }, "/", null));
|
try std.testing.expectError(error.RouteMismatch, testCase("/:id", struct { id: usize }, "/", .{}));
|
||||||
try std.testing.expectError(error.RouteMismatch, testCase("/abcd/:id", struct { id: usize }, "/123", null));
|
try std.testing.expectError(error.RouteMismatch, testCase("/abcd/:id", struct { id: usize }, "/123", .{}));
|
||||||
try std.testing.expectError(error.RouteMismatch, testCase("/:id", struct { id: usize }, "/3/id/blahblah", null));
|
try std.testing.expectError(error.RouteMismatch, testCase("/:id", struct { id: usize }, "/3/id/blahblah", .{ .id = 3 }));
|
||||||
try std.testing.expectError(error.InvalidCharacter, testCase("/:id", struct { id: usize }, "/xyz", null));
|
try std.testing.expectError(error.InvalidCharacter, testCase("/:id", struct { id: usize }, "/xyz", .{}));
|
||||||
}
|
}
|
||||||
|
|
||||||
const BaseContentType = enum {
|
const BaseContentType = enum {
|
||||||
|
@ -709,7 +715,7 @@ pub fn ParseBody(comptime Body: type, comptime options: ParseBodyOptions) type {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub fn parseBody(comptime Body: type, comptime options: ParseBodyOptions) ParseBody(Body, options) {
|
pub fn parseBody(comptime Body: type) ParseBody(Body) {
|
||||||
return .{};
|
return .{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,7 +752,7 @@ test "parseBody" {
|
||||||
var headers = http.Fields.init(std.testing.allocator);
|
var headers = http.Fields.init(std.testing.allocator);
|
||||||
defer headers.deinit();
|
defer headers.deinit();
|
||||||
|
|
||||||
try parseBody(Struct, .{}).handle(
|
try parseBody(Struct).handle(
|
||||||
.{ .body = @as(?std.io.StreamSource, stream), .headers = headers },
|
.{ .body = @as(?std.io.StreamSource, stream), .headers = headers },
|
||||||
.{},
|
.{},
|
||||||
.{ .allocator = std.testing.allocator },
|
.{ .allocator = std.testing.allocator },
|
||||||
|
|
|
@ -357,6 +357,6 @@ test "parseFormData" {
|
||||||
var src = std.io.StreamSource{ .const_buffer = std.io.fixedBufferStream(body) };
|
var src = std.io.StreamSource{ .const_buffer = std.io.fixedBufferStream(body) };
|
||||||
const val = try parseFormData(struct {
|
const val = try parseFormData(struct {
|
||||||
foo: []const u8,
|
foo: []const u8,
|
||||||
}, false, "abcd", src.reader(), std.testing.allocator);
|
}, "abcd", src.reader(), std.testing.allocator);
|
||||||
util.deepFree(std.testing.allocator, val);
|
util.deepFree(std.testing.allocator, val);
|
||||||
}
|
}
|
||||||
|
|
|
@ -252,49 +252,49 @@ fn formatQuery(comptime prefix: []const u8, params: anytype, writer: anytype) !v
|
||||||
|
|
||||||
test "parse" {
|
test "parse" {
|
||||||
const testCase = struct {
|
const testCase = struct {
|
||||||
fn case(allow_unknown_fields: bool, comptime T: type, expected: T, query_string: []const u8) !void {
|
fn case(comptime T: type, expected: T, query_string: []const u8) !void {
|
||||||
const result = try parse(std.testing.allocator, allow_unknown_fields, T, query_string);
|
const result = try parse(std.testing.allocator, T, query_string);
|
||||||
defer parseFree(std.testing.allocator, result);
|
defer parseFree(std.testing.allocator, result);
|
||||||
try util.testing.expectDeepEqual(expected, result);
|
try util.testing.expectDeepEqual(expected, result);
|
||||||
}
|
}
|
||||||
}.case;
|
}.case;
|
||||||
|
|
||||||
try testCase(false, struct { int: usize = 3 }, .{ .int = 3 }, "");
|
try testCase(struct { int: usize = 3 }, .{ .int = 3 }, "");
|
||||||
try testCase(false, struct { int: usize = 3 }, .{ .int = 2 }, "int=2");
|
try testCase(struct { int: usize = 3 }, .{ .int = 2 }, "int=2");
|
||||||
try testCase(false, struct { int: usize = 3 }, .{ .int = 2 }, "int=2&");
|
try testCase(struct { int: usize = 3 }, .{ .int = 2 }, "int=2&");
|
||||||
try testCase(false, struct { boolean: bool = false }, .{ .boolean = false }, "");
|
try testCase(struct { boolean: bool = false }, .{ .boolean = false }, "");
|
||||||
try testCase(false, struct { boolean: bool = false }, .{ .boolean = true }, "boolean");
|
try testCase(struct { boolean: bool = false }, .{ .boolean = true }, "boolean");
|
||||||
try testCase(false, struct { boolean: bool = false }, .{ .boolean = true }, "boolean=true");
|
try testCase(struct { boolean: bool = false }, .{ .boolean = true }, "boolean=true");
|
||||||
try testCase(false, struct { boolean: bool = false }, .{ .boolean = true }, "boolean=y");
|
try testCase(struct { boolean: bool = false }, .{ .boolean = true }, "boolean=y");
|
||||||
try testCase(false, struct { boolean: bool = false }, .{ .boolean = false }, "boolean=f");
|
try testCase(struct { boolean: bool = false }, .{ .boolean = false }, "boolean=f");
|
||||||
try testCase(false, struct { boolean: bool = false }, .{ .boolean = false }, "boolean=no");
|
try testCase(struct { boolean: bool = false }, .{ .boolean = false }, "boolean=no");
|
||||||
try testCase(false, struct { str_enum: ?enum { foo, bar } = null }, .{ .str_enum = null }, "");
|
try testCase(struct { str_enum: ?enum { foo, bar } = null }, .{ .str_enum = null }, "");
|
||||||
try testCase(false, struct { str_enum: ?enum { foo, bar } = null }, .{ .str_enum = .foo }, "str_enum=foo");
|
try testCase(struct { str_enum: ?enum { foo, bar } = null }, .{ .str_enum = .foo }, "str_enum=foo");
|
||||||
try testCase(false, struct { str_enum: ?enum { foo, bar } = null }, .{ .str_enum = .bar }, "str_enum=bar");
|
try testCase(struct { str_enum: ?enum { foo, bar } = null }, .{ .str_enum = .bar }, "str_enum=bar");
|
||||||
try testCase(false, struct { str_enum: ?enum { foo, bar } = .foo }, .{ .str_enum = .foo }, "");
|
try testCase(struct { str_enum: ?enum { foo, bar } = .foo }, .{ .str_enum = .foo }, "");
|
||||||
try testCase(false, struct { str_enum: ?enum { foo, bar } = .foo }, .{ .str_enum = null }, "str_enum");
|
try testCase(struct { str_enum: ?enum { foo, bar } = .foo }, .{ .str_enum = null }, "str_enum");
|
||||||
try testCase(false, struct { n1: usize = 5, n2: usize = 5 }, .{ .n1 = 1, .n2 = 2 }, "n1=1&n2=2");
|
try testCase(struct { n1: usize = 5, n2: usize = 5 }, .{ .n1 = 1, .n2 = 2 }, "n1=1&n2=2");
|
||||||
try testCase(false, struct { n1: usize = 5, n2: usize = 5 }, .{ .n1 = 1, .n2 = 2 }, "n1=1&n2=2&");
|
try testCase(struct { n1: usize = 5, n2: usize = 5 }, .{ .n1 = 1, .n2 = 2 }, "n1=1&n2=2&");
|
||||||
try testCase(false, struct { n1: usize = 5, n2: usize = 5 }, .{ .n1 = 1, .n2 = 2 }, "n1=1&&n2=2&");
|
try testCase(struct { n1: usize = 5, n2: usize = 5 }, .{ .n1 = 1, .n2 = 2 }, "n1=1&&n2=2&");
|
||||||
|
|
||||||
try testCase(false, struct { str: ?[]const u8 = null }, .{ .str = null }, "");
|
try testCase(struct { str: ?[]const u8 = null }, .{ .str = null }, "");
|
||||||
try testCase(false, struct { str: ?[]const u8 = null }, .{ .str = null }, "str");
|
try testCase(struct { str: ?[]const u8 = null }, .{ .str = null }, "str");
|
||||||
try testCase(false, struct { str: ?[]const u8 = null }, .{ .str = null }, "str=");
|
try testCase(struct { str: ?[]const u8 = null }, .{ .str = null }, "str=");
|
||||||
try testCase(false, struct { str: ?[]const u8 = null }, .{ .str = "foo" }, "str=foo");
|
try testCase(struct { str: ?[]const u8 = null }, .{ .str = "foo" }, "str=foo");
|
||||||
try testCase(false, struct { str: ?[]const u8 = "foo" }, .{ .str = "foo" }, "str=foo");
|
try testCase(struct { str: ?[]const u8 = "foo" }, .{ .str = "foo" }, "str=foo");
|
||||||
try testCase(false, struct { str: ?[]const u8 = "foo" }, .{ .str = "foo" }, "");
|
try testCase(struct { str: ?[]const u8 = "foo" }, .{ .str = "foo" }, "");
|
||||||
try testCase(false, struct { str: ?[]const u8 = "foo" }, .{ .str = null }, "str");
|
try testCase(struct { str: ?[]const u8 = "foo" }, .{ .str = null }, "str");
|
||||||
try testCase(false, struct { str: ?[]const u8 = "foo" }, .{ .str = null }, "str=");
|
try testCase(struct { str: ?[]const u8 = "foo" }, .{ .str = null }, "str=");
|
||||||
|
|
||||||
const rand_uuid = comptime util.Uuid.parse("c1fb6578-4d0c-4eb9-9f67-d56da3ae6f5d") catch unreachable;
|
const rand_uuid = comptime util.Uuid.parse("c1fb6578-4d0c-4eb9-9f67-d56da3ae6f5d") catch unreachable;
|
||||||
try testCase(false, struct { id: ?util.Uuid = null }, .{ .id = null }, "");
|
try testCase(struct { id: ?util.Uuid = null }, .{ .id = null }, "");
|
||||||
try testCase(false, struct { id: ?util.Uuid = null }, .{ .id = null }, "id=");
|
try testCase(struct { id: ?util.Uuid = null }, .{ .id = null }, "id=");
|
||||||
try testCase(false, struct { id: ?util.Uuid = null }, .{ .id = null }, "id");
|
try testCase(struct { id: ?util.Uuid = null }, .{ .id = null }, "id");
|
||||||
try testCase(false, struct { id: ?util.Uuid = null }, .{ .id = rand_uuid }, "id=" ++ rand_uuid.toCharArray());
|
try testCase(struct { id: ?util.Uuid = null }, .{ .id = rand_uuid }, "id=" ++ rand_uuid.toCharArray());
|
||||||
try testCase(false, struct { id: ?util.Uuid = rand_uuid }, .{ .id = rand_uuid }, "");
|
try testCase(struct { id: ?util.Uuid = rand_uuid }, .{ .id = rand_uuid }, "");
|
||||||
try testCase(false, struct { id: ?util.Uuid = rand_uuid }, .{ .id = null }, "id=");
|
try testCase(struct { id: ?util.Uuid = rand_uuid }, .{ .id = null }, "id=");
|
||||||
try testCase(false, struct { id: ?util.Uuid = rand_uuid }, .{ .id = null }, "id");
|
try testCase(struct { id: ?util.Uuid = rand_uuid }, .{ .id = null }, "id");
|
||||||
try testCase(false, struct { id: ?util.Uuid = rand_uuid }, .{ .id = rand_uuid }, "id=" ++ rand_uuid.toCharArray());
|
try testCase(struct { id: ?util.Uuid = rand_uuid }, .{ .id = rand_uuid }, "id=" ++ rand_uuid.toCharArray());
|
||||||
|
|
||||||
const SubStruct = struct {
|
const SubStruct = struct {
|
||||||
sub: struct {
|
sub: struct {
|
||||||
|
@ -302,9 +302,9 @@ test "parse" {
|
||||||
bar: usize = 2,
|
bar: usize = 2,
|
||||||
} = .{},
|
} = .{},
|
||||||
};
|
};
|
||||||
try testCase(false, SubStruct, .{ .sub = .{ .foo = 1, .bar = 2 } }, "");
|
try testCase(SubStruct, .{ .sub = .{ .foo = 1, .bar = 2 } }, "");
|
||||||
try testCase(false, SubStruct, .{ .sub = .{ .foo = 3, .bar = 3 } }, "sub.foo=3&sub.bar=3");
|
try testCase(SubStruct, .{ .sub = .{ .foo = 3, .bar = 3 } }, "sub.foo=3&sub.bar=3");
|
||||||
try testCase(false, SubStruct, .{ .sub = .{ .foo = 3, .bar = 2 } }, "sub.foo=3");
|
try testCase(SubStruct, .{ .sub = .{ .foo = 3, .bar = 2 } }, "sub.foo=3");
|
||||||
|
|
||||||
// TODO: Semantics are ill-defined here. What happens if the substruct doesn't have
|
// TODO: Semantics are ill-defined here. What happens if the substruct doesn't have
|
||||||
// default values?
|
// default values?
|
||||||
|
@ -313,8 +313,8 @@ test "parse" {
|
||||||
// foo: usize = 1,
|
// foo: usize = 1,
|
||||||
// } = null,
|
// } = null,
|
||||||
// };
|
// };
|
||||||
// try testCase(false, SubStruct2, .{ .sub = null }, "");
|
// try testCase(SubStruct2, .{ .sub = null }, "");
|
||||||
// try testCase(false, SubStruct2, .{ .sub = null }, "sub=");
|
// try testCase(SubStruct2, .{ .sub = null }, "sub=");
|
||||||
|
|
||||||
// TODO: also here (semantics are well defined it just breaks tests)
|
// TODO: also here (semantics are well defined it just breaks tests)
|
||||||
// const SubUnion = struct {
|
// const SubUnion = struct {
|
||||||
|
@ -323,24 +323,21 @@ test "parse" {
|
||||||
// bar: usize,
|
// bar: usize,
|
||||||
// } = null,
|
// } = null,
|
||||||
// };
|
// };
|
||||||
// try testCase(false, SubUnion, .{ .sub = null }, "");
|
// try testCase(SubUnion, .{ .sub = null }, "");
|
||||||
// try testCase(false, SubUnion, .{ .sub = null }, "sub=");
|
// try testCase(SubUnion, .{ .sub = null }, "sub=");
|
||||||
|
|
||||||
const SubUnion2 = struct {
|
const SubUnion2 = struct {
|
||||||
sub: ?union(enum) {
|
sub: ?struct {
|
||||||
bar: struct {
|
foo: usize,
|
||||||
foo: usize,
|
val: union(enum) {
|
||||||
bar: []const u8,
|
bar: []const u8,
|
||||||
},
|
|
||||||
baz: struct {
|
|
||||||
foo: usize,
|
|
||||||
baz: []const u8,
|
baz: []const u8,
|
||||||
},
|
},
|
||||||
} = null,
|
} = null,
|
||||||
};
|
};
|
||||||
try testCase(false, SubUnion2, .{ .sub = null }, "");
|
try testCase(SubUnion2, .{ .sub = null }, "");
|
||||||
try testCase(false, SubUnion2, .{ .sub = .{ .bar = .{ .foo = 1, .bar = "abc" } } }, "sub.foo=1&sub.bar=abc");
|
try testCase(SubUnion2, .{ .sub = .{ .foo = 1, .val = .{ .bar = "abc" } } }, "sub.foo=1&sub.bar=abc");
|
||||||
try testCase(false, SubUnion2, .{ .sub = .{ .baz = .{ .foo = 1, .baz = "abc" } } }, "sub.foo=1&sub.baz=abc");
|
try testCase(SubUnion2, .{ .sub = .{ .foo = 1, .val = .{ .baz = "abc" } } }, "sub.foo=1&sub.baz=abc");
|
||||||
}
|
}
|
||||||
|
|
||||||
test "encodeStruct" {
|
test "encodeStruct" {
|
||||||
|
|
|
@ -1349,7 +1349,7 @@ test "template" {
|
||||||
|
|
||||||
try testCase("", .{}, "");
|
try testCase("", .{}, "");
|
||||||
try testCase("abcd", .{}, "abcd");
|
try testCase("abcd", .{}, "abcd");
|
||||||
try testCase("{.val}", .{ .val = @as(usize, 3) }, "3");
|
try testCase("{.val}", .{ .val = 3 }, "3");
|
||||||
try testCase("{#if .val}1{/if}", .{ .val = true }, "1");
|
try testCase("{#if .val}1{/if}", .{ .val = true }, "1");
|
||||||
try testCase("{#for .vals |$v|=} {$v} {=/for}", .{ .vals = [_]u8{ 1, 2, 3 } }, "123");
|
try testCase("{#for .vals |$v|=} {$v} {=/for}", .{ .vals = [_]u8{ 1, 2, 3 } }, "123");
|
||||||
try testCase("{#for .vals |$val|}{$val}{/for}", .{ .vals = [_]u8{ 1, 2, 3 } }, "123");
|
try testCase("{#for .vals |$val|}{$val}{/for}", .{ .vals = [_]u8{ 1, 2, 3 } }, "123");
|
||||||
|
|
|
@ -266,7 +266,7 @@ pub fn DeserializerContext(comptime Result: type, comptime From: type, comptime
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(self: *@This(), allocator: std.mem.Allocator) !Result {
|
pub fn finish(self: *@This(), allocator: std.mem.Allocator) !Result {
|
||||||
return (try self.deserialize(allocator, Result, self.data, &.{}, true)) orelse
|
return (try self.deserialize(allocator, Result, self.data, &.{})) orelse
|
||||||
if (std.meta.fields(Result).len == 0)
|
if (std.meta.fields(Result).len == 0)
|
||||||
return .{}
|
return .{}
|
||||||
else
|
else
|
||||||
|
@ -290,7 +290,6 @@ pub fn DeserializerContext(comptime Result: type, comptime From: type, comptime
|
||||||
comptime T: type,
|
comptime T: type,
|
||||||
intermediary: anytype,
|
intermediary: anytype,
|
||||||
comptime field_ref: FieldRef,
|
comptime field_ref: FieldRef,
|
||||||
allow_default: bool,
|
|
||||||
) DeserializeError!?T {
|
) DeserializeError!?T {
|
||||||
if (comptime Context.options.isScalar(T)) {
|
if (comptime Context.options.isScalar(T)) {
|
||||||
const val = @field(intermediary.static, util.comptimeJoin(".", field_ref));
|
const val = @field(intermediary.static, util.comptimeJoin(".", field_ref));
|
||||||
|
@ -305,7 +304,7 @@ pub fn DeserializerContext(comptime Result: type, comptime From: type, comptime
|
||||||
var partial_match_found: bool = false;
|
var partial_match_found: bool = false;
|
||||||
inline for (info.fields) |field| {
|
inline for (info.fields) |field| {
|
||||||
const F = field.field_type;
|
const F = field.field_type;
|
||||||
const maybe_value = self.deserialize(allocator, F, intermediary, field_ref, false) catch |err| switch (err) {
|
const maybe_value = self.deserialize(allocator, F, intermediary, field_ref) catch |err| switch (err) {
|
||||||
error.MissingField => blk: {
|
error.MissingField => blk: {
|
||||||
partial_match_found = true;
|
partial_match_found = true;
|
||||||
break :blk @as(?F, null);
|
break :blk @as(?F, null);
|
||||||
|
@ -334,7 +333,7 @@ pub fn DeserializerContext(comptime Result: type, comptime From: type, comptime
|
||||||
inline for (info.fields) |field, i| {
|
inline for (info.fields) |field, i| {
|
||||||
const F = field.field_type;
|
const F = field.field_type;
|
||||||
const new_field_ref = field_ref ++ &[_][]const u8{field.name};
|
const new_field_ref = field_ref ++ &[_][]const u8{field.name};
|
||||||
const maybe_value = try self.deserialize(allocator, F, intermediary, new_field_ref, false);
|
const maybe_value = try self.deserialize(allocator, F, intermediary, new_field_ref);
|
||||||
if (maybe_value) |v| {
|
if (maybe_value) |v| {
|
||||||
@field(result, field.name) = v;
|
@field(result, field.name) = v;
|
||||||
fields_alloced[i] = true;
|
fields_alloced[i] = true;
|
||||||
|
@ -351,7 +350,7 @@ pub fn DeserializerContext(comptime Result: type, comptime From: type, comptime
|
||||||
}
|
}
|
||||||
if (any_missing and any_explicit) return error.MissingField;
|
if (any_missing and any_explicit) return error.MissingField;
|
||||||
|
|
||||||
if (!any_explicit and !allow_default) {
|
if (!any_explicit) {
|
||||||
inline for (info.fields) |field, i| {
|
inline for (info.fields) |field, i| {
|
||||||
if (fields_alloced[i]) self.deserializeFree(allocator, @field(result, field.name));
|
if (fields_alloced[i]) self.deserializeFree(allocator, @field(result, field.name));
|
||||||
}
|
}
|
||||||
|
@ -370,7 +369,7 @@ pub fn DeserializerContext(comptime Result: type, comptime From: type, comptime
|
||||||
var count: usize = 0;
|
var count: usize = 0;
|
||||||
errdefer for (result[0..count]) |res| util.deepFree(allocator, res);
|
errdefer for (result[0..count]) |res| util.deepFree(allocator, res);
|
||||||
for (data.items) |sub, i| {
|
for (data.items) |sub, i| {
|
||||||
result[i] = (try self.deserialize(allocator, info.child, sub, &.{}, false)) orelse return error.SparseSlice;
|
result[i] = (try self.deserialize(allocator, info.child, sub, &.{})) orelse return error.SparseSlice;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -379,7 +378,7 @@ pub fn DeserializerContext(comptime Result: type, comptime From: type, comptime
|
||||||
},
|
},
|
||||||
|
|
||||||
// Specifically non-scalar optionals
|
// Specifically non-scalar optionals
|
||||||
.Optional => |info| return try self.deserialize(allocator, info.child, intermediary, field_ref, allow_default),
|
.Optional => |info| return try self.deserialize(allocator, info.child, intermediary, field_ref),
|
||||||
|
|
||||||
else => @compileError("Unsupported type"),
|
else => @compileError("Unsupported type"),
|
||||||
}
|
}
|
||||||
|
@ -407,8 +406,7 @@ test "Deserializer" {
|
||||||
{
|
{
|
||||||
const T = struct { foo: []const u8, bar: bool };
|
const T = struct { foo: []const u8, bar: bool };
|
||||||
|
|
||||||
var ds = Deserializer(T){ .arena = std.heap.ArenaAllocator.init(std.testing.allocator) };
|
var ds = Deserializer(T){};
|
||||||
defer ds.deinit();
|
|
||||||
try ds.setSerializedField("foo", "123");
|
try ds.setSerializedField("foo", "123");
|
||||||
try ds.setSerializedField("bar", "true");
|
try ds.setSerializedField("bar", "true");
|
||||||
|
|
||||||
|
@ -421,8 +419,7 @@ test "Deserializer" {
|
||||||
{
|
{
|
||||||
const T = struct { foo: []const u8, bar: bool };
|
const T = struct { foo: []const u8, bar: bool };
|
||||||
|
|
||||||
var ds = Deserializer(T){ .arena = std.heap.ArenaAllocator.init(std.testing.allocator) };
|
var ds = Deserializer(T){};
|
||||||
defer ds.deinit();
|
|
||||||
try std.testing.expectError(error.UnknownField, ds.setSerializedField("baz", "123"));
|
try std.testing.expectError(error.UnknownField, ds.setSerializedField("baz", "123"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,8 +429,7 @@ test "Deserializer" {
|
||||||
foo: struct { bar: bool, baz: bool },
|
foo: struct { bar: bool, baz: bool },
|
||||||
};
|
};
|
||||||
|
|
||||||
var ds = Deserializer(T){ .arena = std.heap.ArenaAllocator.init(std.testing.allocator) };
|
var ds = Deserializer(T){};
|
||||||
defer ds.deinit();
|
|
||||||
try ds.setSerializedField("foo.bar", "true");
|
try ds.setSerializedField("foo.bar", "true");
|
||||||
try ds.setSerializedField("foo.baz", "true");
|
try ds.setSerializedField("foo.baz", "true");
|
||||||
|
|
||||||
|
@ -442,45 +438,29 @@ test "Deserializer" {
|
||||||
try util.testing.expectDeepEqual(T{ .foo = .{ .bar = true, .baz = true } }, val);
|
try util.testing.expectDeepEqual(T{ .foo = .{ .bar = true, .baz = true } }, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Union behavior
|
// Union embedding
|
||||||
{
|
{
|
||||||
const T = struct {
|
const T = struct {
|
||||||
foo: union(enum) {
|
foo: union(enum) { bar: bool, baz: bool },
|
||||||
bar: struct {
|
|
||||||
bar: bool,
|
|
||||||
},
|
|
||||||
baz: struct {
|
|
||||||
baz: bool,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var ds = Deserializer(T){ .arena = std.heap.ArenaAllocator.init(std.testing.allocator) };
|
var ds = Deserializer(T){};
|
||||||
defer ds.deinit();
|
try ds.setSerializedField("bar", "true");
|
||||||
try ds.setSerializedField("foo.bar", "true");
|
|
||||||
|
|
||||||
const val = try ds.finish(std.testing.allocator);
|
const val = try ds.finish(std.testing.allocator);
|
||||||
defer ds.finishFree(std.testing.allocator, val);
|
defer ds.finishFree(std.testing.allocator, val);
|
||||||
try util.testing.expectDeepEqual(T{ .foo = .{ .bar = .{ .bar = true } } }, val);
|
try util.testing.expectDeepEqual(T{ .foo = .{ .bar = true } }, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns error if multiple union fields specified
|
// Returns error if multiple union fields specified
|
||||||
{
|
{
|
||||||
const T = struct {
|
const T = struct {
|
||||||
foo: union(enum) {
|
foo: union(enum) { bar: bool, baz: bool },
|
||||||
bar: struct {
|
|
||||||
bar: bool,
|
|
||||||
},
|
|
||||||
baz: struct {
|
|
||||||
baz: bool,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var ds = Deserializer(T){ .arena = std.heap.ArenaAllocator.init(std.testing.allocator) };
|
var ds = Deserializer(T){};
|
||||||
defer ds.deinit();
|
try ds.setSerializedField("bar", "true");
|
||||||
try ds.setSerializedField("foo.bar", "true");
|
try ds.setSerializedField("baz", "true");
|
||||||
try ds.setSerializedField("foo.baz", "true");
|
|
||||||
|
|
||||||
try std.testing.expectError(error.DuplicateUnionMember, ds.finish(std.testing.allocator));
|
try std.testing.expectError(error.DuplicateUnionMember, ds.finish(std.testing.allocator));
|
||||||
}
|
}
|
||||||
|
@ -489,8 +469,7 @@ test "Deserializer" {
|
||||||
{
|
{
|
||||||
const T = struct { foo: []const u8 = "123", bar: bool = true };
|
const T = struct { foo: []const u8 = "123", bar: bool = true };
|
||||||
|
|
||||||
var ds = Deserializer(T){ .arena = std.heap.ArenaAllocator.init(std.testing.allocator) };
|
var ds = Deserializer(T){};
|
||||||
defer ds.deinit();
|
|
||||||
|
|
||||||
const val = try ds.finish(std.testing.allocator);
|
const val = try ds.finish(std.testing.allocator);
|
||||||
defer ds.finishFree(std.testing.allocator, val);
|
defer ds.finishFree(std.testing.allocator, val);
|
||||||
|
@ -501,8 +480,7 @@ test "Deserializer" {
|
||||||
{
|
{
|
||||||
const T = struct { foo: []const u8, bar: bool };
|
const T = struct { foo: []const u8, bar: bool };
|
||||||
|
|
||||||
var ds = Deserializer(T){ .arena = std.heap.ArenaAllocator.init(std.testing.allocator) };
|
var ds = Deserializer(T){};
|
||||||
defer ds.deinit();
|
|
||||||
try ds.setSerializedField("foo", "123");
|
try ds.setSerializedField("foo", "123");
|
||||||
|
|
||||||
try std.testing.expectError(error.MissingField, ds.finish(std.testing.allocator));
|
try std.testing.expectError(error.MissingField, ds.finish(std.testing.allocator));
|
||||||
|
@ -515,8 +493,7 @@ test "Deserializer" {
|
||||||
qux: ?union(enum) { quux: usize } = null,
|
qux: ?union(enum) { quux: usize } = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
var ds = Deserializer(T){ .arena = std.heap.ArenaAllocator.init(std.testing.allocator) };
|
var ds = Deserializer(T){};
|
||||||
defer ds.deinit();
|
|
||||||
|
|
||||||
const val = try ds.finish(std.testing.allocator);
|
const val = try ds.finish(std.testing.allocator);
|
||||||
defer ds.finishFree(std.testing.allocator, val);
|
defer ds.finishFree(std.testing.allocator, val);
|
||||||
|
@ -529,10 +506,9 @@ test "Deserializer" {
|
||||||
qux: ?union(enum) { quux: usize } = null,
|
qux: ?union(enum) { quux: usize } = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
var ds = Deserializer(T){ .arena = std.heap.ArenaAllocator.init(std.testing.allocator) };
|
var ds = Deserializer(T){};
|
||||||
defer ds.deinit();
|
|
||||||
try ds.setSerializedField("foo.baz", "3");
|
try ds.setSerializedField("foo.baz", "3");
|
||||||
try ds.setSerializedField("qux", "3");
|
try ds.setSerializedField("quux", "3");
|
||||||
|
|
||||||
const val = try ds.finish(std.testing.allocator);
|
const val = try ds.finish(std.testing.allocator);
|
||||||
defer ds.finishFree(std.testing.allocator, val);
|
defer ds.finishFree(std.testing.allocator, val);
|
||||||
|
@ -545,10 +521,9 @@ test "Deserializer" {
|
||||||
qux: ?union(enum) { quux: usize } = null,
|
qux: ?union(enum) { quux: usize } = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
var ds = Deserializer(T){ .arena = std.heap.ArenaAllocator.init(std.testing.allocator) };
|
var ds = Deserializer(T){};
|
||||||
defer ds.deinit();
|
|
||||||
try ds.setSerializedField("foo.bar", "3");
|
try ds.setSerializedField("foo.bar", "3");
|
||||||
try ds.setSerializedField("qux", "3");
|
try ds.setSerializedField("quux", "3");
|
||||||
|
|
||||||
try std.testing.expectError(error.MissingField, ds.finish(std.testing.allocator));
|
try std.testing.expectError(error.MissingField, ds.finish(std.testing.allocator));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue