pub const Templates = std.StringHashMap(Template); pub var templates: Templates = undefined; pub fn start(app: *App) !void { const allocator = app.allocator; var server = try httpz.ServerCtx(*const Dispatcher, *Env).init(allocator, .{ .address = "0.0.0.0", .port = 5882, }, undefined); defer server.deinit(); templates = Templates.init(allocator); defer { var it = templates.valueIterator(); while (it.next()) |t| { t.deinit(allocator); } templates.deinit(); } inline for (config.templates) |template| { try templates.put(template, try loadTemplate(allocator, template)); } server.dispatcher(Dispatcher.dispatch); var router = server.router(); { var routes = router.group("", .{ .ctx = &Dispatcher{ .app = app } }); routes.get("/", index); routes.get("/about", about); routes.get("/drafts/:id", @import("drafts/show.zig").handler); routes.get("/drafts/:id/pick", @import("drafts/pick.zig").handler); routes.get("/leagues/:id", @import("leagues/show.zig").handler); routes.post("/leagues", @import("leagues/create.zig").handler); routes.get("/invites/:id", @import("leagues/invite.zig").handler); } // use get/post/put/head/patch/options/delete // you can also use "all" to attach to all methods // start the server in the current thread, blocking. try server.listen(); } fn pick(_: *Env, req: *httpz.Request, res: *httpz.Response) !void { try render("pick", req, res); } fn about(_: *Env, req: *httpz.Request, res: *httpz.Response) !void { // try render(templates.get("about").?, req, res); try renderWithData("about", .{ .title = "foo" }, req, res); } fn index(_: *Env, req: *httpz.Request, res: *httpz.Response) !void { try render("index", req, res); } fn loadTemplate(allocator: std.mem.Allocator, comptime template_name: []const u8) !Template { return switch (try mustache.parseText(allocator, @embedFile("../views/" ++ template_name ++ ".mustache"), .{}, .{ .copy_strings = false })) { .success => |t| t, .parse_error => @panic("layout parse error"), }; } fn loadTemplateFile(allocator: std.mem.Allocator, template_name: []const u8) !Template { const file = std.fs.cwd().openFile(try std.fmt.allocPrint(allocator, "src/views/{s}.mustache", .{template_name}), .{}) catch @panic("could not find template"); defer file.close(); const template_bytes = file.readToEndAlloc(allocator, std.math.maxInt(usize)) catch @panic("couldn't read template file "); return switch (try mustache.parseText(allocator, template_bytes, .{}, .{ .copy_strings = false })) { .success => |t| t, .parse_error => @panic("layout parse error"), }; } pub fn renderWithDataPartials(template_name: []const u8, partials: anytype, data: anytype, req: *httpz.Request, res: *httpz.Response) !void { var template: Template = undefined; if (config.dev) { template = try loadTemplateFile(res.arena, template_name); } else { template = templates.get(template_name).?; } const content = try mustache.allocRenderPartials(res.arena, template, partials, data); if (req.header("hx-request")) |_| { res.body = content; } else { res.body = try mustache.allocRender(res.arena, templates.get("layout").?, .{ .content = content }); } } pub fn renderWithData(template_name: []const u8, data: anytype, req: *httpz.Request, res: *httpz.Response) !void { try renderWithDataPartials(template_name, .{}, data, req, res); } pub fn render(template_name: []const u8, req: *httpz.Request, res: *httpz.Response) !void { try renderWithData(template_name, .{}, req, res); } const config = @import("config"); const App = @import("../app.zig").App; const Env = @import("../env.zig").Env; const httpz = @import("httpz"); const mustache = @import("mustache"); const Template = mustache.Template; const std = @import("std"); const Dispatcher = @import("dispatcher.zig").Dispatcher;