From 9557bebf0ba1ae755f6c76e9713edfbbb9e3a249 Mon Sep 17 00:00:00 2001 From: Rene Schallner Date: Mon, 27 Feb 2023 00:56:29 +0100 Subject: [PATCH] mustache rendering works - cleanup needed --- examples/mustache/mustache.zig | 60 +++++++++++++++ src/mustache.zig | 132 ++++++++++++++++++++++++++++++++- src/util.zig | 2 +- 3 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 examples/mustache/mustache.zig diff --git a/examples/mustache/mustache.zig b/examples/mustache/mustache.zig new file mode 100644 index 0000000..897de1e --- /dev/null +++ b/examples/mustache/mustache.zig @@ -0,0 +1,60 @@ +const std = @import("std"); +const zap = @import("zap"); + +fn on_request_verbose(r: zap.SimpleRequest) void { + if (r.path) |the_path| { + std.debug.print("PATH: {s}\n", .{the_path}); + } + + if (r.query) |the_query| { + std.debug.print("QUERY: {s}\n", .{the_query}); + } + const template = "{{=<< >>=}}* Users:\r\n<<#users>><>. <<& name>> (<>)\r\n<>\r\nNested: <<& nested.item >>."; + const p = zap.MustacheNew(template) catch return; + const User = struct { + name: []const u8, + id: isize, + }; + std.debug.print("{*}\n", .{p}); + if (zap.MustacheBuild(p, .{ + .users = [_]User{ + User{ + .name = "Rene", + .id = 1, + }, + User{ + .name = "Caro", + .id = 6, + }, + }, + .nested = .{ + .item = "nesting works", + }, + })) |s| { + std.debug.print("{s}\n", .{s}); + _ = r.sendBody(s); + } + _ = r.sendBody("

Hello from ZAP!!!

"); +} + +fn on_request_minimal(r: zap.SimpleRequest) void { + _ = r.sendBody("

Hello from ZAP!!!

"); +} + +pub fn main() !void { + var listener = zap.SimpleHttpListener.init(.{ + .port = 3000, + .on_request = on_request_verbose, + .log = true, + .max_clients = 100000, + }); + try listener.listen(); + + std.debug.print("Listening on 0.0.0.0:3000\n", .{}); + + // start worker threads + zap.start(.{ + .threads = 1, + .workers = 1, + }); +} diff --git a/src/mustache.zig b/src/mustache.zig index 76bdd82..f0d7375 100644 --- a/src/mustache.zig +++ b/src/mustache.zig @@ -7,6 +7,7 @@ // @cInclude("fiobj_mustache.h"); // }); +const std = @import("std"); const util = @import("util.zig"); pub const FIOBJ = usize; @@ -87,11 +88,29 @@ pub fn MustacheNew(data: []const u8) MustacheError!*Mustache { // 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_free(arg_o: FIOBJ) callconv(.C) void; +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_num_new(num: isize) callconv(.C) FIOBJ; + +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; // this build is slow because it needs to translate to a FIOBJ data // object FIOBJ_T_HASH -pub fn MustacheBuild(mustache: *Mustache, data: ?FIOBJ) ?[]const u8 { - _ = data; +pub fn MustacheBuild(mustache: *Mustache, data: anytype) ?[]const u8 { // FIOBJ data = fiobj_hash_new(); // FIOBJ key = fiobj_str_new("users", 5); @@ -121,11 +140,116 @@ pub fn MustacheBuild(mustache: *Mustache, data: ?FIOBJ) ?[]const u8 { // fiobj_hash_set(ary, key, fiobj_str_new("dot notation success", 20)); // fiobj_free(key); - var empty = fiobj_hash_new(); - var ret = fiobj_mustache_build(mustache, empty); + const T = @TypeOf(data); + if (@typeInfo(T) != .Struct) { + @compileError("No struct: '" ++ @typeName(T) ++ "'"); + } + + // std.debug.print("data: ", .{}); + const fiobj_data = fiobjectify(data); + // std.debug.print("{any}\n", .{fiobj_data}); + + // TODO: fiobj_free everything + var ret = fiobj_mustache_build(mustache, fiobj_data); return util.fio2str(ret); } +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); + } + 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 extern fn fiobj_mustache_free(mustache: ?*mustache_s) void; pub fn MustacheFree(m: ?*Mustache) void { fiobj_mustache_free(m); diff --git a/src/util.zig b/src/util.zig index fae4c8d..01c0dcb 100644 --- a/src/util.zig +++ b/src/util.zig @@ -19,7 +19,7 @@ pub fn str2fio(s: []const u8) C.fio_str_info_s { }; } -fn toCharPtr(s: []const u8) [*c]u8 { +pub fn toCharPtr(s: []const u8) [*c]u8 { return @intToPtr([*c]u8, @ptrToInt(s.ptr)); }