about summary refs log tree commit diff
path: root/src/main.zig
blob: 8e515ee9fae1dae645fe660f297ba5b7a7f98903 (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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// TODO: just let compiler infer error set through recursive calls
// depends on: https://github.com/ziglang/zig/issues/2971

// This code is heavily insprired by Devine's implementation in C:
// https://github.com/hundredrabbits/100r.co/blob/main/src/main.c and his
// personal site https://github.com/XXIIVV/Oscean

const std = @import("std");
const gen = @import("gen.zig");

const log = std.log.scoped(.root);

// tune std.log options
pub const std_options = std.Options{
    .log_level = .info,
};

pub const Context = struct {
    arena: std.heap.ArenaAllocator,

    pages: [][]const u8 = undefined,
    generated: usize = 0,
    meta: usize = 0,
    tilde: usize = 0,

    pub const params = struct {
        pub const PAGE_PATH = "src/inc";
        pub const SITE_PATH = "site";
        pub const LINK_PATH = "links";

        pub const SITE_NAME = "gzr.im";
        pub const LICENSE_HOLDER = "robertgzr";
        pub const LICENSE_YEAR = "2025";
        pub const LICENSE_NAME = "BY-NC-SA 4.0";
        pub const LICENSE_URL = "https://git.sr.ht/~robertgzr/site/blob/master/LICENSE.content";
        pub const SOURCE_URL = "https://git.sr.ht/~robertgzr/site/tree/master";
        pub const PUBLIC_INBOX = "~robertgzr/public-inbox@lists.sr.ht";
    };

    fn new(parent_alloc: std.mem.Allocator) *Context {
        const ctx = parent_alloc.create(Context) catch |err| @panic(@errorName(err));
        ctx.* = .{
            .arena = std.heap.ArenaAllocator.init(parent_alloc),
        };
        return ctx;
    }

    fn deinit(self: *Context) void {
        self.arena.deinit();
        self.arena.child_allocator.destroy(self);
    }

    pub fn allocator(self: *Context) std.mem.Allocator {
        return self.arena.allocator();
    }
};

fn index(ctx: *Context) !void {
    var root = std.fs.cwd().openDir(Context.params.PAGE_PATH, .{ .iterate = true }) catch |err| switch (err) {
        error.FileNotFound => {
            std.log.err("Missing {s} folder.", .{Context.params.PAGE_PATH});
            std.os.linux.exit(1);
        },
        else => |e| return e,
    };
    defer root.close();

    var pages = std.ArrayList([]const u8).init(ctx.allocator());
    defer pages.deinit();

    var walk = try root.walk(ctx.allocator());
    defer walk.deinit();

    while (try walk.next()) |e| {
        switch (e.kind) {
            .file => {
                if (std.mem.endsWith(u8, e.basename, ".htm")) {
                    std.log.debug("Indexing {s}", .{e.path});
                    try pages.append(try ctx.allocator().dupe(u8, e.path));
                }
            },
            else => {},
        }
    }

    ctx.pages = try pages.toOwnedSlice();

    return;
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();
    defer _ = gpa.deinit();

    var ctx = Context.new(allocator);
    defer ctx.deinit();

    index(ctx) catch {
        log.err("Failed to index pages.", .{});
        std.os.linux.exit(1);
    };

    log.info("Indexed {d} pages.", .{ctx.pages.len});

    Cli.parse()(ctx);
}

const RunFn = fn (*Context) void;

const Cli = struct {
    fn parse() *const RunFn {
        var argv = std.process.args();

        const more = argv.skip(); // skip arg0
        if (!more) return &Cli.help_cmd;

        const next_arg = argv.next() orelse return &Cli.help_cmd;

        if (std.mem.eql(u8, next_arg, "gen")) {
            return &Cli.gen_cmd;
        } else {
            return &Cli.help_cmd;
        }
    }

    fn help_cmd(_: *Context) void {
        std.debug.print("usage: rbl [gen]\n", .{});
        std.os.linux.exit(0);
    }

    fn gen_cmd(ctx: *Context) void {
        gen.generate(ctx) catch {
            log.err("Failed to generate site.", .{});
            std.os.linux.exit(1);
        };
    }
};