summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Ashby <martin@ashbysoft.com>2023-08-06 21:38:52 +0100
committerMartin Ashby <martin@ashbysoft.com>2023-08-06 21:38:52 +0100
commit17d8cc65fe6396d505dda6bf162942cab9e00408 (patch)
tree8b86fd2e6d5a365cacece8a05ff3d90444a30bc2
parent8156e741ef7c8a59317ba801aa0c8940e9fcbd00 (diff)
downloadzigwebserver-17d8cc65fe6396d505dda6bf162942cab9e00408.tar.gz
zigwebserver-17d8cc65fe6396d505dda6bf162942cab9e00408.tar.bz2
zigwebserver-17d8cc65fe6396d505dda6bf162942cab9e00408.tar.xz
zigwebserver-17d8cc65fe6396d505dda6bf162942cab9e00408.zip
Add structure parsing to form/query handling
-rw-r--r--src/zigwebserver.zig81
1 files changed, 81 insertions, 0 deletions
diff --git a/src/zigwebserver.zig b/src/zigwebserver.zig
index 42188f8..2e20382 100644
--- a/src/zigwebserver.zig
+++ b/src/zigwebserver.zig
@@ -394,6 +394,14 @@ pub const Path = struct {
}
return self.query_parsed.?.data.get(key);
}
+
+ pub fn query_to_struct(self: *Path, comptime T: type) !T {
+ if (self.query_parsed == null) {
+ self.query_parsed = try Form.parse(self.allocator, self.query);
+ }
+ return self.query_parsed.?.form_to_struct(T);
+ }
+
pub fn deinit(self: *Path) void {
if (self.query_parsed != null) {
self.query_parsed.?.deinit();
@@ -453,6 +461,21 @@ const PathTest = struct {
try std.testing.expect(v2 != null);
try std.testing.expectEqualSlices(u8, "bo om", v2.?);
}
+
+ test "query to struct" {
+ var p = try Path.parse(std.testing.allocator, "/foo?bar=ba+z&bam=55&zigzag#frag");
+ defer p.deinit();
+ const T = struct {
+ bar: []const u8,
+ bam: u64,
+ fn deinit(self: *@This()) void {
+ std.testing.allocator.free(self.bar);
+ }
+ };
+ var t = try p.query_to_struct(T);
+ defer t.deinit();
+ try std.testing.expectEqualDeep(T{ .bar = "ba z", .bam = 55 }, t);
+ }
};
pub const Form = struct {
@@ -482,6 +505,9 @@ pub const Form = struct {
}
return Form{ .allocator = allocator, .data = res };
}
+ pub fn form_to_struct(self: *Form, comptime T: type) !T {
+ return to_struct(self.allocator, T, self.data);
+ }
pub fn deinit(self: *Form) void {
var it = self.data.iterator();
var e = it.next();
@@ -524,8 +550,63 @@ const PercentEncodeTest = struct {
}
};
+/// Populate a struct from a hashmap
+fn to_struct(allocator: std.mem.Allocator, comptime T: type, hm: std.StringHashMap([]const u8)) !T {
+ const ti = @typeInfo(T);
+ if (ti != .Struct) {
+ @compileError("to_struct T was not a struct type");
+ }
+ var t: T = undefined;
+ inline for (ti.Struct.fields) |field| {
+ if (field.is_comptime) {
+ @compileError("can't dynamically set comptime field " ++ field.name);
+ }
+ const value: []const u8 = hm.get(field.name) orelse {
+ return error.FieldNotPresent; // TODO somehow be more useful.
+ };
+ switch (@typeInfo(field.type)) { // TODO handle more types, default values etc etc.
+ .Int => {
+ @field(t, field.name) = try std.fmt.parseInt(field.type, value, 10);
+ },
+ .Pointer => |ptrinfo| {
+ if (ptrinfo.size != .Slice) {
+ @compileError("field pointer size " ++ @tagName(ptrinfo.size) ++ " is not supported, only []u8 is supported right now");
+ }
+ if (ptrinfo.child != u8) {
+ @compileError("field pointer type " ++ @tagName(@typeInfo(ptrinfo.child)) ++ " is not supported, only []u8 is supported right now");
+ }
+ const dvalue = try allocator.dupe(u8, value);
+ errdefer allocator.free(dvalue);
+ @field(t, field.name) = dvalue;
+ },
+ else => @compileError("field type " ++ @tagName(@typeInfo(field.type)) ++ " not supported on field " ++ field.name),
+ }
+ }
+ return t;
+}
+
+const StructTest = struct {
+ test "to struct" {
+ const T = struct {
+ foo: i64,
+ bar: []const u8,
+ pub fn deinit(self: *@This()) void {
+ std.testing.allocator.free(self.bar);
+ }
+ };
+ var hm = std.StringHashMap([]const u8).init(std.testing.allocator);
+ defer hm.deinit();
+ try hm.put("foo", "42");
+ try hm.put("bar", "oops");
+ var t = try to_struct(std.testing.allocator, T, hm);
+ defer t.deinit();
+ try std.testing.expectEqualDeep(T{ .foo = 42, .bar = "oops" }, t);
+ }
+};
+
test {
_ = RouterTest;
_ = PathTest;
_ = PercentEncodeTest;
+ _ = StructTest;
}