1
0
Fork 0
mirror of https://github.com/zigzap/zap.git synced 2025-10-21 07:34:08 +00:00

Updated mustache.zig to be more Zig like - mustache.zig now returns a Mustache struct. This allows functions to be called on the mustache instance, e.g. zap.mustacheFree(mustache) -> mustache.free(). - Updated mustache example. - Added doc strings to majority of mustache functions.

This commit is contained in:
Brook Jeynes 2023-12-30 13:15:07 +10:00 committed by Rene Schallner
parent 57370b4e31
commit 9c19038082
2 changed files with 250 additions and 223 deletions

View file

@ -1,15 +1,26 @@
const std = @import("std"); const std = @import("std");
const zap = @import("zap"); const zap = @import("zap");
const Mustache = @import("zap").Mustache;
fn on_request(r: zap.SimpleRequest) void { fn on_request(r: zap.SimpleRequest) void {
const template = "{{=<< >>=}}* Users:\r\n<<#users>><<id>>. <<& name>> (<<name>>)\r\n<</users>>\r\nNested: <<& nested.item >>."; const template =
const p = zap.mustacheData(template) catch return; \\ {{=<< >>=}}
defer zap.mustacheFree(p); \\ * Users:
\\ <<#users>>
\\ <<id>>. <<& name>> (<<name>>)
\\ <</users>>
\\ Nested: <<& nested.item >>.
;
var mustache = Mustache.fromData(template) catch return;
defer mustache.deinit();
const User = struct { const User = struct {
name: []const u8, name: []const u8,
id: isize, id: isize,
}; };
const ret = zap.mustacheBuild(p, .{
const ret = mustache.build(.{
.users = [_]User{ .users = [_]User{
.{ .{
.name = "Rene", .name = "Rene",
@ -25,6 +36,7 @@ fn on_request(r: zap.SimpleRequest) void {
}, },
}); });
defer ret.deinit(); defer ret.deinit();
if (r.setContentType(.TEXT)) { if (r.setContentType(.TEXT)) {
if (ret.str()) |s| { if (ret.str()) |s| {
r.sendBody(s) catch return; r.sendBody(s) catch return;

View file

@ -1,11 +1,14 @@
// 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 std = @import("std");
const fio = @import("fio.zig"); const fio = @import("fio.zig");
const util = @import("util.zig"); const util = @import("util.zig");
/// A struct to handle Mustache templating.
///
/// This is a wrapper around fiobj's mustache template handling.
/// See http://facil.io/0.7.x/fiobj_mustache for more information.
pub const Mustache = struct {
const Self = @This();
pub const struct_mustache_s = opaque {}; pub const struct_mustache_s = opaque {};
pub const enum_mustache_error_en = c_uint; pub const enum_mustache_error_en = c_uint;
pub const mustache_error_en = enum_mustache_error_en; pub const mustache_error_en = enum_mustache_error_en;
@ -16,16 +19,17 @@ pub extern fn fiobj_mustache_build(mustache: ?*mustache_s, data: fio.FIOBJ) fio.
pub extern fn fiobj_mustache_build2(dest: fio.FIOBJ, mustache: ?*mustache_s, data: fio.FIOBJ) fio.FIOBJ; pub extern fn fiobj_mustache_build2(dest: fio.FIOBJ, mustache: ?*mustache_s, data: fio.FIOBJ) fio.FIOBJ;
pub extern fn fiobj_mustache_free(mustache: ?*mustache_s) void; pub extern fn fiobj_mustache_free(mustache: ?*mustache_s) void;
/// Mustache load args used in mustacheNew /// Load arguments used when creating a new Mustache instance.
/// One source of data must be passed in, otherwise an error will occur.
pub const MustacheLoadArgs = struct { pub const MustacheLoadArgs = struct {
/// optional filename, enables partial templates on filesystem /// Filename. This enables partial templates on filesystem.
filename: ?[]const u8 = null, filename: ?[]const u8 = null,
/// optional string data. should be used if no filename is specified. /// String data. Should be used if no filename is specified.
data: ?[]const u8 = null, data: ?[]const u8 = null,
}; };
// used internally for interfacing with fio /// Internal struct used for interfacing with fio.
const MustacheLoadArgsFio = extern struct { const MustacheLoadArgsFio = extern struct {
filename: [*c]const u8, filename: [*c]const u8,
filename_len: usize, filename_len: usize,
@ -34,11 +38,10 @@ const MustacheLoadArgsFio = extern struct {
err: [*c]mustache_error_en, err: [*c]mustache_error_en,
}; };
pub const Mustache = mustache_s; handler: *mustache_s,
pub const MustacheStatus = enum(c_int) {}; pub const Status = enum(c_int) {};
pub const Error = error{
pub const MustacheError = error{
MUSTACHE_ERR_TOO_DEEP, MUSTACHE_ERR_TOO_DEEP,
MUSTACHE_ERR_CLOSURE_MISMATCH, MUSTACHE_ERR_CLOSURE_MISMATCH,
MUSTACHE_ERR_FILE_NOT_FOUND, MUSTACHE_ERR_FILE_NOT_FOUND,
@ -52,12 +55,12 @@ pub const MustacheError = error{
MUSTACHE_ERR_USER_ERROR, MUSTACHE_ERR_USER_ERROR,
}; };
// pub extern fn fiobj_mustache_build2(dest: FIOBJ, mustache: ?*mustache_s, data: FIOBJ) FIOBJ; /// Create a new `Mustache` instance; `deinit()` should be called to free
/// the object after usage.
pub fn mustacheNew(load_args: MustacheLoadArgs) MustacheError!*Mustache { pub fn init(load_args: MustacheLoadArgs) Error!Self {
var err: mustache_error_en = undefined; var err: mustache_error_en = undefined;
var args: MustacheLoadArgsFio = .{ const args: MustacheLoadArgsFio = .{
.filename = filn: { .filename = filn: {
if (load_args.filename) |filn| break :filn filn.ptr else break :filn null; if (load_args.filename) |filn| break :filn filn.ptr else break :filn null;
}, },
@ -73,58 +76,73 @@ pub fn mustacheNew(load_args: MustacheLoadArgs) MustacheError!*Mustache {
.err = &err, .err = &err,
}; };
var ret = fiobj_mustache_new(args); const ret = fiobj_mustache_new(args);
switch (err) { switch (err) {
0 => return ret.?, 0 => return Self{
1 => return MustacheError.MUSTACHE_ERR_TOO_DEEP, .handler = ret.?,
2 => return MustacheError.MUSTACHE_ERR_CLOSURE_MISMATCH, },
3 => return MustacheError.MUSTACHE_ERR_FILE_NOT_FOUND, 1 => return Error.MUSTACHE_ERR_TOO_DEEP,
4 => return MustacheError.MUSTACHE_ERR_FILE_TOO_BIG, 2 => return Error.MUSTACHE_ERR_CLOSURE_MISMATCH,
5 => return MustacheError.MUSTACHE_ERR_FILE_NAME_TOO_LONG, 3 => return Error.MUSTACHE_ERR_FILE_NOT_FOUND,
6 => return MustacheError.MUSTACHE_ERR_FILE_NAME_TOO_SHORT, 4 => return Error.MUSTACHE_ERR_FILE_TOO_BIG,
7 => return MustacheError.MUSTACHE_ERR_EMPTY_TEMPLATE, 5 => return Error.MUSTACHE_ERR_FILE_NAME_TOO_LONG,
8 => return MustacheError.MUSTACHE_ERR_DELIMITER_TOO_LONG, 6 => return Error.MUSTACHE_ERR_FILE_NAME_TOO_SHORT,
9 => return MustacheError.MUSTACHE_ERR_NAME_TOO_LONG, 7 => return Error.MUSTACHE_ERR_EMPTY_TEMPLATE,
10 => return MustacheError.MUSTACHE_ERR_UNKNOWN, 8 => return Error.MUSTACHE_ERR_DELIMITER_TOO_LONG,
11 => return MustacheError.MUSTACHE_ERR_USER_ERROR, 9 => return Error.MUSTACHE_ERR_NAME_TOO_LONG,
else => return MustacheError.MUSTACHE_ERR_UNKNOWN, 10 => return Error.MUSTACHE_ERR_UNKNOWN,
11 => return Error.MUSTACHE_ERR_USER_ERROR,
else => return Error.MUSTACHE_ERR_UNKNOWN,
} }
return MustacheError.MUSTACHE_ERR_UNKNOWN; return Error.MUSTACHE_ERR_UNKNOWN;
} }
/// Convenience function if you only want to use in-memory data (as opposed to file data) /// Convenience function to create a new `Mustache` instance with in-memory data loaded;
pub fn mustacheData(data: []const u8) MustacheError!*Mustache { /// `deinit()` should be called to free the object after usage..
return mustacheNew(.{ .data = data }); pub fn fromData(data: []const u8) Error!Mustache {
return Self.init(.{ .data = data });
} }
/// Convenience function if you only want to use file-based data (as opposed to in-memory data) /// Convenience function to create a new `Mustache` instance with file-based data loaded;
pub fn mustacheLoad(filename: []const u8) MustacheError!*Mustache { /// `deinit()` should be called to free the object after usage..
return mustacheNew(.{ .filename = filename }); pub fn fromFile(filename: []const u8) Error!Mustache {
return Self.init(.{ .filename = filename });
} }
// implement these: fiobj_mustache.c /// Free the data backing a `Mustache` instance.
pub fn deinit(self: *Self) void {
fiobj_mustache_free(self.handler);
}
// TODO: implement these - fiobj_mustache.c
// pub extern fn fiobj_mustache_build(mustache: ?*mustache_s, data: FIOBJ) FIOBJ; // 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_build2(dest: FIOBJ, mustache: ?*mustache_s, data: FIOBJ) FIOBJ;
/// The result from calling `build`.
const MustacheBuildResult = struct { const MustacheBuildResult = struct {
fiobj_result: fio.FIOBJ = 0, fiobj_result: fio.FIOBJ = 0,
/// holds the context converted into a fiobj, used in build /// Holds the context converted into a fiobj.
/// This is used in `build`.
fiobj_context: fio.FIOBJ = 0, fiobj_context: fio.FIOBJ = 0,
/// Free the data backing a `MustacheBuildResult` instance.
pub fn deinit(m: *const MustacheBuildResult) void { pub fn deinit(m: *const MustacheBuildResult) void {
fio.fiobj_free_wrapped(m.fiobj_result); fio.fiobj_free_wrapped(m.fiobj_result);
fio.fiobj_free_wrapped(m.fiobj_context); fio.fiobj_free_wrapped(m.fiobj_context);
} }
/// Retrieve a string representation of the built template.
pub fn str(m: *const MustacheBuildResult) ?[]const u8 { pub fn str(m: *const MustacheBuildResult) ?[]const u8 {
return util.fio2str(m.fiobj_result); return util.fio2str(m.fiobj_result);
} }
}; };
// this build is slow because it needs to translate to a FIOBJ data /// Build the Mustache template; `deinit()` should be called on the build
// object FIOBJ_T_HASH /// result to free the data.
pub fn mustacheBuild(mustache: *Mustache, data: anytype) MustacheBuildResult { /// TODO: This build is slow because it needs to translate to a FIOBJ data
/// object - FIOBJ_T_HASH
pub fn build(self: *Self, data: anytype) MustacheBuildResult {
const T = @TypeOf(data); const T = @TypeOf(data);
if (@typeInfo(T) != .Struct) { if (@typeInfo(T) != .Struct) {
@compileError("No struct: '" ++ @typeName(T) ++ "'"); @compileError("No struct: '" ++ @typeName(T) ++ "'");
@ -133,7 +151,7 @@ pub fn mustacheBuild(mustache: *Mustache, data: anytype) MustacheBuildResult {
var result: MustacheBuildResult = .{}; var result: MustacheBuildResult = .{};
result.fiobj_context = fiobjectify(data); result.fiobj_context = fiobjectify(data);
result.fiobj_result = fiobj_mustache_build(mustache, result.fiobj_context); result.fiobj_result = fiobj_mustache_build(self.handler, result.fiobj_context);
return result; return result;
} }
@ -178,7 +196,7 @@ pub fn fiobjectify(
}, },
.Struct => |S| { .Struct => |S| {
// create a new fio hashmap // create a new fio hashmap
var m = fio.fiobj_hash_new(); const m = fio.fiobj_hash_new();
// std.debug.print("new struct\n", .{}); // std.debug.print("new struct\n", .{});
inline for (S.fields) |Field| { inline for (S.fields) |Field| {
// don't include void fields // don't include void fields
@ -215,7 +233,7 @@ pub fn fiobjectify(
return fio.fiobj_str_new(util.toCharPtr(value), value.len); return fio.fiobj_str_new(util.toCharPtr(value), value.len);
} }
var arr = fio.fiobj_ary_new2(value.len); const arr = fio.fiobj_ary_new2(value.len);
for (value) |x| { for (value) |x| {
const v = fiobjectify(x); const v = fiobjectify(x);
fio.fiobj_ary_push(arr, v); fio.fiobj_ary_push(arr, v);
@ -233,7 +251,4 @@ pub fn fiobjectify(
} }
unreachable; unreachable;
} }
};
pub fn mustacheFree(m: ?*Mustache) void {
fiobj_mustache_free(m);
}