Form File support

This commit is contained in:
jaina heartles 2022-12-02 22:34:12 -08:00
parent e6f57495c0
commit 2206cd6ac9
2 changed files with 37 additions and 10 deletions

View file

@ -110,6 +110,11 @@ const MultipartFormField = struct {
content_type: ?[]const u8 = null,
};
const FormFile = struct {
data: []const u8,
filename: []const u8,
};
pub fn MultipartForm(comptime ReaderType: type) type {
return struct {
stream: MultipartStream(ReaderType),
@ -145,27 +150,49 @@ pub fn openForm(multipart_stream: anytype) MultipartForm(@TypeOf(multipart_strea
return .{ .stream = multipart_stream };
}
fn Deserializer(comptime Result: type) type {
return util.DeserializerContext(Result, MultipartFormField, struct {
pub const options = .{ .isScalar = isScalar, .embed_unions = true };
pub fn isScalar(comptime T: type) bool {
if (T == FormFile or T == ?FormFile) return true;
return util.serialize.defaultIsScalar(T);
}
pub fn deserializeScalar(_: @This(), alloc: std.mem.Allocator, comptime T: type, val: MultipartFormField) !T {
if (T == FormFile or T == ?FormFile) return try deserializeFormFile(alloc, val);
if (val.filename != null) return error.FilenameProvidedForNonFile;
return try util.serialize.deserializeString(alloc, T, val.value);
}
fn deserializeFormFile(alloc: std.mem.Allocator, val: MultipartFormField) !FormFile {
const data = try util.deepClone(alloc, val.value);
errdefer util.deepFree(alloc, data);
const filename = try util.deepClone(alloc, val.filename orelse "(untitled)");
return FormFile{
.data = data,
.filename = filename,
};
}
});
}
pub fn parseFormData(comptime T: type, boundary: []const u8, reader: anytype, alloc: std.mem.Allocator) !T {
var form = openForm(try openMultipart(boundary, reader));
var ds = util.Deserializer(T){};
var ds = Deserializer(T){};
defer {
var iter = ds.iterator();
while (iter.next()) |pair| {
alloc.free(pair.value);
util.deepFree(alloc, pair.value);
}
}
while (true) {
var part = (try form.next(alloc)) orelse break;
errdefer util.deepFree(alloc, part);
try ds.setSerializedField(part.name, part.value);
alloc.free(part.name);
// TODO:
if (part.content_type) |v| alloc.free(v);
if (part.filename) |v| alloc.free(v);
try ds.setSerializedField(part.name, part);
}
return try ds.finish(alloc);

View file

@ -1,7 +1,7 @@
const std = @import("std");
const util = @import("./lib.zig");
const FieldRef = []const []const u8;
pub const FieldRef = []const []const u8;
pub fn defaultIsScalar(comptime T: type) bool {
if (comptime std.meta.trait.is(.Optional)(T) and defaultIsScalar(std.meta.Child(T))) return true;