This commit is contained in:
jaina heartles 2022-12-02 21:49:17 -08:00
parent ba4f3a7bf4
commit 0b13f210c7
7 changed files with 112 additions and 42 deletions

View file

@ -1,5 +1,60 @@
const std = @import("std");
pub const ParamIter = struct {
str: []const u8,
index: usize = 0,
const Param = struct {
name: []const u8,
value: []const u8,
};
pub fn from(str: []const u8) ParamIter {
return .{ .str = str, .index = std.mem.indexOfScalar(u8, str, ';') orelse str.len };
}
pub fn fieldValue(self: *ParamIter) []const u8 {
return std.mem.sliceTo(self.str, ';');
}
pub fn next(self: *ParamIter) ?Param {
if (self.index >= self.str.len) return null;
const start = self.index + 1;
const new_start = std.mem.indexOfScalarPos(u8, self.str, start, ';') orelse self.str.len;
self.index = new_start;
const param = std.mem.trim(u8, self.str[start..new_start], " \t");
var split = std.mem.split(u8, param, "=");
const name = split.first();
const value = std.mem.trimLeft(u8, split.rest(), " \t");
// TODO: handle quoted values
// TODO: handle parse errors
return Param{
.name = name,
.value = value,
};
}
};
pub fn getParam(field: []const u8, name: ?[]const u8) ?[]const u8 {
var iter = ParamIter.from(field);
if (name) |param| {
while (iter.next()) |p| {
if (std.ascii.eqlIgnoreCase(param, p.name)) {
const trimmed = std.mem.trim(u8, p.value, " \t");
if (trimmed.len >= 2 and trimmed[0] == '"' and trimmed[trimmed.len - 1] == '"') {
return trimmed[1 .. trimmed.len - 1];
}
return trimmed;
}
}
return null;
} else return iter.fieldValue();
}
pub const Fields = struct {
const HashContext = struct {
const hash_seed = 1;

View file

@ -5,6 +5,7 @@ const server = @import("./server.zig");
pub const urlencode = @import("./urlencode.zig");
pub const socket = @import("./socket.zig");
const json = @import("./json.zig");
pub const fields = @import("./fields.zig");
pub const Method = std.http.Method;
pub const Status = std.http.Status;
@ -16,7 +17,7 @@ pub const Server = server.Server;
pub const middleware = @import("./middleware.zig");
pub const Fields = @import("./headers.zig").Fields;
pub const Fields = fields.Fields;
pub const Protocol = enum {
http_1_0,

View file

@ -1,6 +0,0 @@
test {
_ = @import("./request/test_parser.zig");
_ = @import("./middleware.zig");
_ = @import("./multipart.zig");
_ = @import("./urlencode.zig");
}

View file

@ -144,42 +144,16 @@ fn fieldPtr(ptr: anytype, comptime names: []const []const u8) FieldPtr(@TypeOf(p
return fieldPtr(&@field(ptr.*, names[0]), names[1..]);
}
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 (T == bool) 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 recursiveFieldPaths(comptime T: type, comptime prefix: []const []const u8) []const []const []const u8 {
comptime {
var fields: []const []const []const u8 = &.{};
for (std.meta.fields(T)) |f| {
const full_name = prefix ++ [_][]const u8{f.name};
if (isScalar(f.field_type)) {
fields = fields ++ [_][]const []const u8{full_name};
} else {
fields = fields ++ recursiveFieldPaths(f.field_type, full_name);
}
}
return fields;
}
}
// Represents a set of results.
// row() must be called until it returns null, or the query may not complete
// Must be deallocated by a call to finish()
pub fn Results(comptime T: type) type {
// would normally make this a declaration of the struct, but it causes the compiler to crash
const fields = if (T == void) .{} else recursiveFieldPaths(T, &.{});
const fields = if (T == void) .{} else util.serialize.getRecursiveFieldList(
T,
&.{},
util.serialize.default_options,
);
return struct {
const Self = @This();

View file

@ -601,3 +601,20 @@ const ControlTokenIter = struct {
self.peeked_token = token;
}
};
test "template" {
const testCase = struct {
fn case(comptime tmpl: []const u8, args: anytype, expected: []const u8) !void {
var stream = std.io.changeDetectionStream(expected, std.io.null_writer);
try execute(stream.writer(), tmpl, args);
try std.testing.expect(!stream.changeDetected());
}
}.case;
try testCase("", .{}, "");
try testCase("abcd", .{}, "abcd");
try testCase("{.val}", .{ .val = 3 }, "3");
try testCase("{#if .val}1{/if}", .{ .val = true }, "1");
try testCase("{#for .vals |$v|}{$v}{/for}", .{ .vals = [_]u8{ 1, 2, 3 } }, "123");
try testCase("{#for .vals |$v|=} {$v} {=/for}", .{ .vals = [_]u8{ 1, 2, 3 } }, "123");
}

View file

@ -201,7 +201,7 @@ pub fn seedThreadPrng() !void {
pub fn comptimeToCrlf(comptime str: []const u8) []const u8 {
comptime {
@setEvalBranchQuota(str.len * 6);
@setEvalBranchQuota(str.len * 10);
const size = std.mem.replacementSize(u8, str, "\n", "\r\n");
var buf: [size]u8 = undefined;
_ = std.mem.replace(u8, str, "\n", "\r\n", &buf);

View file

@ -38,7 +38,7 @@ pub fn deserializeString(allocator: std.mem.Allocator, comptime T: type, value:
@compileError("Invalid type " ++ @typeName(T));
}
fn getRecursiveFieldList(comptime T: type, comptime prefix: FieldRef, comptime options: SerializationOptions) []const FieldRef {
pub fn getRecursiveFieldList(comptime T: type, comptime prefix: FieldRef, comptime options: SerializationOptions) []const FieldRef {
comptime {
if (std.meta.trait.is(.Union)(T) and prefix.len == 0 and options.embed_unions) {
@compileError("Cannot embed a union into nothing");
@ -113,7 +113,7 @@ pub fn DeserializerContext(comptime Result: type, comptime From: type, comptime
context: Context = .{},
pub fn setSerializedField(self: *@This(), key: []const u8, value: From) !void {
const field = std.meta.stringToEnum(std.meta.FieldEnum(Data), key);
const field = std.meta.stringToEnum(std.meta.FieldEnum(Data), key) orelse return error.UnknownField;
inline for (comptime std.meta.fieldNames(Data)) |field_name| {
@setEvalBranchQuota(10000);
const f = comptime std.meta.stringToEnum(std.meta.FieldEnum(Data), field_name);
@ -123,7 +123,36 @@ pub fn DeserializerContext(comptime Result: type, comptime From: type, comptime
}
}
return error.UnknownField;
unreachable;
}
pub const Iter = struct {
data: *const Data,
field_index: usize,
const Item = struct {
key: []const u8,
value: From,
};
pub fn next(self: *Iter) ?Item {
while (self.field_index < std.meta.fields(Data).len) {
const idx = self.field_index;
self.field_index += 1;
inline for (comptime std.meta.fieldNames(Data)) |field, i| {
if (i == idx) {
const maybe_value = @field(self.data.*, field);
if (maybe_value) |value| return Item{ .key = field, .value = value };
}
}
}
return null;
}
};
pub fn iterator(self: *const @This()) Iter {
return .{ .data = &self.data, .field_index = 0 };
}
pub fn finishFree(_: *@This(), allocator: std.mem.Allocator, val: anytype) void {