Form File support
This commit is contained in:
parent
e6f57495c0
commit
2206cd6ac9
2 changed files with 37 additions and 10 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue