aboutsummaryrefslogtreecommitdiffstats
path: root/src/web/web.zig
blob: 184a1a37ecb943abfb9630a9c6131adf14be5924 (plain) (blame)
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;