mirror of
https://github.com/zigzap/zap.git
synced 2025-10-20 23:24:09 +00:00

- zap.Request : refactored into its own file, along with supporting types and functions (e.g. http params related) - added setContentTypeFromFilename thx @hauleth. - zap.Auth : zap.Auth.Basic, zap.Auth.BearerSingle, ... - zap.Endpoint : zap.Endpoint, zap.Endpoint.Authenticating
250 lines
8.9 KiB
Zig
250 lines
8.9 KiB
Zig
const std = @import("std");
|
|
const fio = @import("fio.zig");
|
|
const util = @import("util.zig");
|
|
|
|
const Self = @This();
|
|
|
|
const struct_mustache_s = opaque {};
|
|
const mustache_s = struct_mustache_s;
|
|
const enum_mustache_error_en = c_uint;
|
|
const mustache_error_en = enum_mustache_error_en;
|
|
|
|
extern fn fiobj_mustache_new(args: MustacheLoadArgsFio) ?*mustache_s;
|
|
extern fn fiobj_mustache_build(mustache: ?*mustache_s, data: fio.FIOBJ) fio.FIOBJ;
|
|
extern fn fiobj_mustache_build2(dest: fio.FIOBJ, mustache: ?*mustache_s, data: fio.FIOBJ) fio.FIOBJ;
|
|
extern fn fiobj_mustache_free(mustache: ?*mustache_s) void;
|
|
|
|
/// Load arguments used when creating a new Mustache instance.
|
|
pub const MustacheLoadArgs = struct {
|
|
/// Filename. This enables partial templates on filesystem.
|
|
filename: ?[]const u8 = null,
|
|
|
|
/// String data. Should be used if no filename is specified.
|
|
data: ?[]const u8 = null,
|
|
};
|
|
|
|
/// Internal struct used for interfacing with fio.
|
|
const MustacheLoadArgsFio = extern struct {
|
|
filename: [*c]const u8,
|
|
filename_len: usize,
|
|
data: [*c]const u8,
|
|
data_len: usize,
|
|
err: [*c]mustache_error_en,
|
|
};
|
|
|
|
/// Handle to the underlying fiobj mustache instance.
|
|
handle: *mustache_s,
|
|
|
|
pub const Error = error{
|
|
MUSTACHE_ERR_TOO_DEEP,
|
|
MUSTACHE_ERR_CLOSURE_MISMATCH,
|
|
MUSTACHE_ERR_FILE_NOT_FOUND,
|
|
MUSTACHE_ERR_FILE_TOO_BIG,
|
|
MUSTACHE_ERR_FILE_NAME_TOO_LONG,
|
|
MUSTACHE_ERR_FILE_NAME_TOO_SHORT,
|
|
MUSTACHE_ERR_EMPTY_TEMPLATE,
|
|
MUSTACHE_ERR_DELIMITER_TOO_LONG,
|
|
MUSTACHE_ERR_NAME_TOO_LONG,
|
|
MUSTACHE_ERR_UNKNOWN,
|
|
MUSTACHE_ERR_USER_ERROR,
|
|
};
|
|
|
|
/// Create a new `Mustache` instance; `deinit()` should be called to free
|
|
/// the object after usage.
|
|
pub fn init(load_args: MustacheLoadArgs) Error!Self {
|
|
var err: mustache_error_en = undefined;
|
|
|
|
const args: MustacheLoadArgsFio = .{
|
|
.filename = filn: {
|
|
if (load_args.filename) |filn| break :filn filn.ptr else break :filn null;
|
|
},
|
|
.filename_len = filn_len: {
|
|
if (load_args.filename) |filn| break :filn_len filn.len else break :filn_len 0;
|
|
},
|
|
.data = data: {
|
|
if (load_args.data) |data| break :data data.ptr else break :data null;
|
|
},
|
|
.data_len = data_len: {
|
|
if (load_args.data) |data| break :data_len data.len else break :data_len 0;
|
|
},
|
|
.err = &err,
|
|
};
|
|
|
|
const ret = fiobj_mustache_new(args);
|
|
switch (err) {
|
|
0 => return Self{
|
|
.handle = ret.?,
|
|
},
|
|
1 => return Error.MUSTACHE_ERR_TOO_DEEP,
|
|
2 => return Error.MUSTACHE_ERR_CLOSURE_MISMATCH,
|
|
3 => return Error.MUSTACHE_ERR_FILE_NOT_FOUND,
|
|
4 => return Error.MUSTACHE_ERR_FILE_TOO_BIG,
|
|
5 => return Error.MUSTACHE_ERR_FILE_NAME_TOO_LONG,
|
|
6 => return Error.MUSTACHE_ERR_FILE_NAME_TOO_SHORT,
|
|
7 => return Error.MUSTACHE_ERR_EMPTY_TEMPLATE,
|
|
8 => return Error.MUSTACHE_ERR_DELIMITER_TOO_LONG,
|
|
9 => return Error.MUSTACHE_ERR_NAME_TOO_LONG,
|
|
10 => return Error.MUSTACHE_ERR_UNKNOWN,
|
|
11 => return Error.MUSTACHE_ERR_USER_ERROR,
|
|
else => return Error.MUSTACHE_ERR_UNKNOWN,
|
|
}
|
|
unreachable;
|
|
}
|
|
|
|
/// Convenience function to create a new `Mustache` instance with in-memory data loaded;
|
|
/// `deinit()` should be called to free the object after usage..
|
|
pub fn fromData(data: []const u8) Error!Self {
|
|
return Self.init(.{ .data = data });
|
|
}
|
|
|
|
/// Convenience function to create a new `Mustache` instance with file-based data loaded;
|
|
/// `deinit()` should be called to free the object after usage..
|
|
pub fn fromFile(filename: []const u8) Error!Self {
|
|
return Self.init(.{ .filename = filename });
|
|
}
|
|
|
|
/// Free the data backing a `Mustache` instance.
|
|
pub fn deinit(self: *Self) void {
|
|
fiobj_mustache_free(self.handle);
|
|
}
|
|
|
|
// TODO: implement these - fiobj_mustache.c
|
|
// pub extern fn fiobj_mustache_build(mustache: ?*mustache_s, data: FIOBJ) FIOBJ;
|
|
// pub extern fn fiobj_mustache_build2(dest: FIOBJ, mustache: ?*mustache_s, data: FIOBJ) FIOBJ;
|
|
|
|
/// The result from calling `build`.
|
|
const MustacheBuildResult = struct {
|
|
fiobj_result: fio.FIOBJ = 0,
|
|
|
|
/// Holds the context converted into a fiobj.
|
|
/// This is used in `build`.
|
|
fiobj_context: fio.FIOBJ = 0,
|
|
|
|
/// Free the data backing a `MustacheBuildResult` instance.
|
|
pub fn deinit(m: *const MustacheBuildResult) void {
|
|
fio.fiobj_free_wrapped(m.fiobj_result);
|
|
fio.fiobj_free_wrapped(m.fiobj_context);
|
|
}
|
|
|
|
/// Retrieve a string representation of the built template.
|
|
pub fn str(m: *const MustacheBuildResult) ?[]const u8 {
|
|
return util.fio2str(m.fiobj_result);
|
|
}
|
|
};
|
|
|
|
/// Build the Mustache template; `deinit()` should be called on the build
|
|
/// result to free the data.
|
|
// TODO: The build may be slow because it needs to convert zig types to facil.io
|
|
// types. However, this needs to be investigated into.
|
|
// See `fiobjectify` for more information.
|
|
pub fn build(self: *Self, data: anytype) MustacheBuildResult {
|
|
const T = @TypeOf(data);
|
|
if (@typeInfo(T) != .Struct) {
|
|
@compileError("No struct: '" ++ @typeName(T) ++ "'");
|
|
}
|
|
|
|
var result: MustacheBuildResult = .{};
|
|
|
|
result.fiobj_context = fiobjectify(data);
|
|
result.fiobj_result = fiobj_mustache_build(self.handle, result.fiobj_context);
|
|
return result;
|
|
}
|
|
|
|
/// Internal function used to convert zig types to facil.io types.
|
|
/// Used when providing the context to `fiobj_mustache_build`.
|
|
fn fiobjectify(
|
|
value: anytype,
|
|
) fio.FIOBJ {
|
|
const T = @TypeOf(value);
|
|
switch (@typeInfo(T)) {
|
|
.Float, .ComptimeFloat => {
|
|
return fio.fiobj_float_new(value);
|
|
},
|
|
.Int, .ComptimeInt => {
|
|
return fio.fiobj_num_new_bignum(value);
|
|
},
|
|
.Bool => {
|
|
return if (value) fio.fiobj_true() else fio.fiobj_false();
|
|
},
|
|
.Null => {
|
|
return 0;
|
|
},
|
|
.Optional => {
|
|
if (value) |payload| {
|
|
return fiobjectify(payload);
|
|
} else {
|
|
return fiobjectify(null);
|
|
}
|
|
},
|
|
.Enum => {
|
|
return fio.fiobj_num_new_bignum(@intFromEnum(value));
|
|
},
|
|
.Union => {
|
|
const info = @typeInfo(T).Union;
|
|
if (info.tag_type) |UnionTagType| {
|
|
inline for (info.fields) |u_field| {
|
|
if (value == @field(UnionTagType, u_field.name)) {
|
|
return fiobjectify(@field(value, u_field.name));
|
|
}
|
|
}
|
|
} else {
|
|
@compileError("Unable to fiobjectify untagged union '" ++ @typeName(T) ++ "'");
|
|
}
|
|
},
|
|
.Struct => |S| {
|
|
// create a new fio hashmap
|
|
const m = fio.fiobj_hash_new();
|
|
// std.debug.print("new struct\n", .{});
|
|
inline for (S.fields) |Field| {
|
|
// don't include void fields
|
|
if (Field.type == void) continue;
|
|
|
|
// std.debug.print(" new field: {s}\n", .{Field.name});
|
|
const fname = fio.fiobj_str_new(util.toCharPtr(Field.name), Field.name.len);
|
|
// std.debug.print(" fiobj name : {any}\n", .{fname});
|
|
const v = @field(value, Field.name);
|
|
// std.debug.print(" value: {any}\n", .{v});
|
|
const fvalue = fiobjectify(v);
|
|
// std.debug.print(" fiobj value: {any}\n", .{fvalue});
|
|
_ = fio.fiobj_hash_set(m, fname, fvalue);
|
|
fio.fiobj_free_wrapped(fname);
|
|
}
|
|
return m;
|
|
},
|
|
.ErrorSet => return fiobjectify(@as([]const u8, @errorName(value))),
|
|
.Pointer => |ptr_info| switch (ptr_info.size) {
|
|
.One => switch (@typeInfo(ptr_info.child)) {
|
|
.Array => {
|
|
const Slice = []const std.meta.Elem(ptr_info.child);
|
|
return fiobjectify(@as(Slice, value));
|
|
},
|
|
else => {
|
|
// TODO: avoid loops?
|
|
return fiobjectify(value.*);
|
|
},
|
|
},
|
|
// TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972)
|
|
.Slice => {
|
|
// std.debug.print("new slice\n", .{});
|
|
if (ptr_info.child == u8 and std.unicode.utf8ValidateSlice(value)) {
|
|
return fio.fiobj_str_new(util.toCharPtr(value), value.len);
|
|
}
|
|
|
|
const arr = fio.fiobj_ary_new2(value.len);
|
|
for (value) |x| {
|
|
const v = fiobjectify(x);
|
|
fio.fiobj_ary_push(arr, v);
|
|
}
|
|
return arr;
|
|
},
|
|
else => @compileError("Unable to fiobjectify type '" ++ @typeName(T) ++ "'"),
|
|
},
|
|
.Array => return fiobjectify(&value),
|
|
.Vector => |info| {
|
|
const array: [info.len]info.child = value;
|
|
return fiobjectify(&array);
|
|
},
|
|
else => @compileError("Unable to fiobjectify type '" ++ @typeName(T) ++ "'"),
|
|
}
|
|
unreachable;
|
|
}
|