Fix parsing for enum and boolean values

This commit is contained in:
jaina heartles 2022-11-26 23:28:00 -08:00
parent 29a38240d9
commit d0e08e4b04

View file

@ -66,10 +66,6 @@ const QueryIter = @import("util").QueryIter;
/// Would be used to parse a query string like /// Would be used to parse a query string like
/// `?foo.baz=12345` /// `?foo.baz=12345`
/// ///
/// Compound types cannot currently be nullable, and must be structs.
///
/// TODO: values are currently case-sensitive, and are not url-decoded properly.
/// This should be fixed.
pub fn parseQuery(alloc: std.mem.Allocator, comptime T: type, query: []const u8) !T { pub fn parseQuery(alloc: std.mem.Allocator, comptime T: type, query: []const u8) !T {
if (comptime !std.meta.trait.isContainer(T)) @compileError("T must be a struct"); if (comptime !std.meta.trait.isContainer(T)) @compileError("T must be a struct");
var iter = QueryIter.from(query); var iter = QueryIter.from(query);
@ -88,7 +84,7 @@ pub fn parseQuery(alloc: std.mem.Allocator, comptime T: type, query: []const u8)
return (try parse(alloc, T, "", "", fields)) orelse error.NoQuery; return (try parse(alloc, T, "", "", fields)) orelse error.NoQuery;
} }
fn decodeString(alloc: std.mem.Allocator, val: []const u8) ![]const u8 { fn decodeString(alloc: std.mem.Allocator, val: []const u8) ![]u8 {
var list = try std.ArrayList(u8).initCapacity(alloc, val.len); var list = try std.ArrayList(u8).initCapacity(alloc, val.len);
errdefer list.deinit(); errdefer list.deinit();
@ -266,20 +262,23 @@ fn parseQueryValueNotNull(alloc: std.mem.Allocator, comptime T: type, value: []c
if (comptime std.meta.trait.isZigString(T)) return decoded; if (comptime std.meta.trait.isZigString(T)) return decoded;
defer alloc.free(decoded);
const result = if (comptime std.meta.trait.isIntegral(T)) const result = if (comptime std.meta.trait.isIntegral(T))
try std.fmt.parseInt(T, decoded, 0) try std.fmt.parseInt(T, decoded, 0)
else if (comptime std.meta.trait.isFloat(T)) else if (comptime std.meta.trait.isFloat(T))
try std.fmt.parseFloat(T, decoded) try std.fmt.parseFloat(T, decoded)
else if (comptime std.meta.trait.is(.Enum)(T)) else if (comptime std.meta.trait.is(.Enum)(T)) blk: {
std.meta.stringToEnum(T, decoded) orelse return error.InvalidEnumValue _ = std.ascii.lowerString(decoded, decoded);
else if (T == bool) break :blk std.meta.stringToEnum(T, decoded) orelse return error.InvalidEnumValue;
bool_map.get(value) orelse return error.InvalidBool } else if (T == bool) blk: {
else if (comptime std.meta.trait.hasFn("parse")(T)) _ = std.ascii.lowerString(decoded, decoded);
break :blk bool_map.get(decoded) orelse return error.InvalidBool;
} else if (comptime std.meta.trait.hasFn("parse")(T))
try T.parse(value) try T.parse(value)
else else
@compileError("Invalid type " ++ @typeName(T)); @compileError("Invalid type " ++ @typeName(T));
alloc.free(decoded);
return result; return result;
} }
@ -359,7 +358,7 @@ fn format(comptime prefix: []const u8, comptime name: []const u8, params: anytyp
} }
} }
test { test "parseQuery" {
const TestQuery = struct { const TestQuery = struct {
int: usize = 3, int: usize = 3,
boolean: bool = false, boolean: bool = false,
@ -370,11 +369,11 @@ test {
.int = 3, .int = 3,
.boolean = false, .boolean = false,
.str_enum = null, .str_enum = null,
}, try parseQuery(TestQuery, "")); }, try parseQuery(std.testing.allocator, TestQuery, ""));
try std.testing.expectEqual(TestQuery{ try std.testing.expectEqual(TestQuery{
.int = 5, .int = 5,
.boolean = true, .boolean = true,
.str_enum = .foo, .str_enum = .foo,
}, try parseQuery(TestQuery, "?int=5&boolean=yes&str_enum=foo")); }, try parseQuery(std.testing.allocator, TestQuery, "?int=5&boolean=yes&str_enum=foo"));
} }