1
0
Fork 0
mirror of https://github.com/zigzap/zap.git synced 2025-10-20 15:14:08 +00:00
zap/src/mustache.zig
2023-02-27 02:15:29 +01:00

221 lines
7.9 KiB
Zig

// supporting code to make using facilio's mustache stuff
// (see http://facil.io/0.7.x/fiobj_mustache)
// easier / possible / more zig-like
const std = @import("std");
const util = @import("util.zig");
pub const FIOBJ = usize;
pub const struct_mustache_s = opaque {};
pub const enum_mustache_error_en = c_uint;
pub const mustache_error_en = enum_mustache_error_en;
pub const mustache_s = struct_mustache_s;
pub extern fn fiobj_mustache_new(args: MustacheLoadArgs) ?*mustache_s;
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;
pub extern fn fiobj_mustache_free(mustache: ?*mustache_s) void;
pub const MustacheLoadArgs = extern struct {
filename: [*c]const u8,
filename_len: usize,
data: [*c]const u8,
data_len: usize,
err: [*c]mustache_error_en,
};
pub const Mustache = mustache_s;
pub const MustacheStatus = enum(c_int) {};
pub const MustacheError = 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,
};
// pub extern fn fiobj_mustache_load(filename: fio_str_info_s) ?*mustache_s;
// pub extern fn fiobj_mustache_build2(dest: FIOBJ, mustache: ?*mustache_s, data: FIOBJ) FIOBJ;
pub fn MustacheNew(data: []const u8) MustacheError!*Mustache {
var err: mustache_error_en = undefined;
var args: MustacheLoadArgs = .{
.filename = null,
.filename_len = 0,
.data = data.ptr,
.data_len = data.len,
.err = &err,
};
var ret = fiobj_mustache_new(args);
switch (err) {
0 => return ret.?,
1 => return MustacheError.MUSTACHE_ERR_TOO_DEEP,
2 => return MustacheError.MUSTACHE_ERR_CLOSURE_MISMATCH,
3 => return MustacheError.MUSTACHE_ERR_FILE_NOT_FOUND,
4 => return MustacheError.MUSTACHE_ERR_FILE_TOO_BIG,
5 => return MustacheError.MUSTACHE_ERR_FILE_NAME_TOO_LONG,
6 => return MustacheError.MUSTACHE_ERR_FILE_NAME_TOO_SHORT,
7 => return MustacheError.MUSTACHE_ERR_EMPTY_TEMPLATE,
8 => return MustacheError.MUSTACHE_ERR_DELIMITER_TOO_LONG,
9 => return MustacheError.MUSTACHE_ERR_NAME_TOO_LONG,
10 => return MustacheError.MUSTACHE_ERR_UNKNOWN,
11 => return MustacheError.MUSTACHE_ERR_USER_ERROR,
else => return MustacheError.MUSTACHE_ERR_UNKNOWN,
}
}
// 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;
pub extern fn fiobj_hash_new() FIOBJ;
pub extern fn fiobj_hash_set(hash: FIOBJ, key: FIOBJ, obj: FIOBJ) c_int;
pub extern fn fiobj_ary_push(ary: FIOBJ, obj: FIOBJ) void;
pub extern fn fiobj_float_new(num: f64) FIOBJ;
pub extern fn fiobj_num_new_bignum(num: isize) FIOBJ;
pub extern fn fiobj_free_wrapped(o: FIOBJ) callconv(.C) void;
pub const FIOBJ_T_TRUE: c_int = 22;
pub const FIOBJ_T_FALSE: c_int = 38;
pub fn fiobj_true() callconv(.C) FIOBJ {
return @bitCast(FIOBJ, @as(c_long, FIOBJ_T_TRUE));
}
pub fn fiobj_false() callconv(.C) FIOBJ {
return @bitCast(FIOBJ, @as(c_long, FIOBJ_T_FALSE));
}
pub extern fn fiobj_ary_new2(capa: usize) FIOBJ;
pub extern fn fiobj_str_new(str: [*c]const u8, len: usize) FIOBJ;
pub extern fn fiobj_str_buf(capa: usize) FIOBJ;
const MustacheBuildResult = struct {
fiobj_result: FIOBJ = 0,
pub fn deinit(m: *const MustacheBuildResult) void {
fiobj_free_wrapped(m.fiobj_result);
}
pub fn str(m: *const MustacheBuildResult) ?[]const u8 {
return util.fio2str(m.fiobj_result);
}
};
// this build is slow because it needs to translate to a FIOBJ data
// object FIOBJ_T_HASH
pub fn MustacheBuild(mustache: *Mustache, data: anytype) MustacheBuildResult {
const T = @TypeOf(data);
if (@typeInfo(T) != .Struct) {
@compileError("No struct: '" ++ @typeName(T) ++ "'");
}
const fiobj_data = fiobjectify(data);
return .{ .fiobj_result = fiobj_mustache_build(mustache, fiobj_data) };
}
pub fn fiobjectify(
value: anytype,
) FIOBJ {
const T = @TypeOf(value);
switch (@typeInfo(T)) {
.Float, .ComptimeFloat => {
return fiobj_float_new(value);
},
.Int, .ComptimeInt => {
return fiobj_num_new_bignum(value);
},
.Bool => {
return if (value) fiobj_true() else fiobj_false();
},
.Null => {
return 0;
},
.Optional => {
if (value) |payload| {
return fiobjectify(payload);
} else {
return fiobjectify(null);
}
},
.Enum => {
return fiobj_num_new_bignum(@enumToInt(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
var m = 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 = 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});
_ = fiobj_hash_set(m, fname, fvalue);
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 fiobj_str_new(util.toCharPtr(value), value.len);
}
var arr = fiobj_ary_new2(value.len);
for (value) |x| {
const v = fiobjectify(x);
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;
}
pub fn MustacheFree(m: ?*Mustache) void {
fiobj_mustache_free(m);
}