1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
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;
|