diff --git a/src/http/multipart.zig b/src/http/multipart.zig index 93ec957..1450153 100644 --- a/src/http/multipart.zig +++ b/src/http/multipart.zig @@ -9,6 +9,11 @@ const FormFieldResult = struct { more: bool, }; +const FormField = struct { + value: []const u8, + params: FormDataParams, +}; + const ParamIter = struct { str: []const u8, index: usize = 0, @@ -163,6 +168,74 @@ pub fn parseFormData(boundary: []const u8, reader: anytype, alloc: std.mem.Alloc if (!field.more) return; } } + +const FormFile = struct { + filename: ?[]const u8, + data: []const u8, +}; + +fn parseFormValue(alloc: std.mem.Allocator, comptime T: type, field: FormField) !T { + const is_optional = std.meta.trait.is(.Optional)(T); + if ((comptime is_optional) and field.value.len == 0) return null; + + if (comptime std.meta.trait.isZigString(T)) return field.value; + + if (T == FormFile) { + return FormFile{ + .filename = field.filename, + .data = field.value, + }; + } + + const result = if (comptime std.meta.trait.isIntegral(T)) + try std.fmt.parseInt(T, field.value, 0) + else if (comptime std.meta.trait.isFloat(T)) + try std.fmt.parseFloat(T, field.value) + else if (comptime std.meta.trait.is(.Enum)(T)) blk: { + const val = std.ascii.lowerStringAlloc(alloc, field.value); + defer alloc.free(val); + break :blk std.meta.stringToEnum(T, val) orelse return error.InvalidEnumValue; + } else if (T == bool) blk: { + const val = std.ascii.lowerStringAlloc(alloc, field.value); + defer alloc.free(val); + break :blk bool_map.get(val) orelse return error.InvalidBool; + } else if (comptime std.meta.trait.hasFn("parse")(T)) + try T.parse(field.value) + else + @compileError("Invalid type " ++ @typeName(T)); + + return result; +} + +const bool_map = std.ComptimeStringMap(bool, .{ + .{ "true", true }, + .{ "t", true }, + .{ "yes", true }, + .{ "y", true }, + .{ "1", true }, + + .{ "false", false }, + .{ "f", false }, + .{ "no", false }, + .{ "n", false }, + .{ "0", false }, +}); + +fn isScalar(comptime T: type) bool { + if (comptime std.meta.trait.isZigString(T)) return true; + if (comptime std.meta.trait.isIntegral(T)) return true; + if (comptime std.meta.trait.isFloat(T)) return true; + if (comptime std.meta.trait.is(.Enum)(T)) return true; + if (comptime std.meta.trait.is(.EnumLiteral)(T)) return true; + if (T == bool) return true; + if (T == FormFile) return true; + if (comptime std.meta.trait.hasFn("parse")(T)) return true; + + if (comptime std.meta.trait.is(.Optional)(T) and isScalar(std.meta.Child(T))) return true; + + return false; +} + fn toCrlf(comptime str: []const u8) []const u8 { comptime { var buf: [str.len * 2]u8 = undefined;