about summary refs log tree commit diff
path: root/src/main.zig
diff options
context:
space:
mode:
authorRobert Günzler <r@gnzler.io>2026-06-24 11:57:02 +0900
committerRobert Günzler <r@gnzler.io>2026-09-22 18:01:30 +0200
commitc677e8873e0eae790a1b8b07844a10efa9f63a1e (patch)
tree95bc9709c374d27962a73712d24807a98cc69bb4 /src/main.zig
parentfcdf352af595759aa34e6ed037ee10810867a616 (diff)
downloadsite-c677e8873e0eae790a1b8b07844a10efa9f63a1e.tar.gz
wip design for v2
Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to '')
-rw-r--r--src/main.zig332
1 files changed, 73 insertions, 259 deletions
diff --git a/src/main.zig b/src/main.zig
index fb8e687..354eed9 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -5,9 +5,8 @@
 // https://github.com/hundredrabbits/100r.co/blob/main/src/main.c and his
 // personal site https://github.com/XXIIVV/Oscean
 
-const exe = @import("exe.zig");
-const gen = @import("gen.zig");
-const fs = @import("fs.zig");
+const Context = @import("Context.zig");
+const os = @import("os.zig");
 
 const std = @import("std");
 
@@ -15,299 +14,114 @@ const log = std.log.scoped(.rbc);
 
 // tune std.log options
 pub const std_options = std.Options{
-    .log_level = .debug,
+    .log_level = if (@import("builtin").mode == .Debug) .debug else .info,
 };
 
-pub const Context = struct {
-    pub const Site = struct {
-        pub const Page = struct {
-            kind: enum {
-                Meta,
-                Htm,
-                Markdown,
-            },
-            path: []const u8,
-            name: []const u8,
-        };
-
-        path: []const u8,
-        pages: std.ArrayListUnmanaged(Page) = .{},
-    };
-
-    arena: std.heap.ArenaAllocator,
-    arena_mx: std.Thread.Mutex = .{},
-    alloc: std.heap.ThreadSafeAllocator = undefined,
-
-    progress_nodes: std.ArrayListUnmanaged(std.Progress.Node) = .{},
-    progress_nodes_mx: std.Thread.Mutex = .{},
-    progress_nodes_tl: ?*const std.Progress.Node = null,
-
-    sites: std.ArrayListUnmanaged(Site) = .{},
-    generated: std.ArrayListUnmanaged([]const u8) = .{},
-    meta: usize = 0,
-    tilde: usize = 0,
-
-    // opts
-    force: bool = false,
-
-    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";
-
-        pub const PANDOC = "pandoc";
-        pub const PANDOC_PREPROC = "preproc.lua";
-    };
-
-    fn create(parent_alloc: std.mem.Allocator) !*Context {
-        const self = try parent_alloc.create(Context);
-        self.* = .{
-            .arena = std.heap.ArenaAllocator.init(parent_alloc),
-        };
-        // create thread safe allocator
-        self.alloc = std.heap.ThreadSafeAllocator{
-            .child_allocator = self.arena.allocator(),
-            .mutex = self.arena_mx,
-        };
-        return self;
-    }
-
-    fn destroy(self: *Context) void {
-        self.progress_end();
-        self.progress_nodes.deinit(self.allocator());
-        self.arena.deinit();
-        self.arena.child_allocator.destroy(self);
-    }
-
-    pub fn allocator(self: *Context) std.mem.Allocator {
-        return self.alloc.allocator();
-    }
-
-    // progress handling
-    pub fn progress_start(self: *Context, name: []const u8, become_toplevel: bool) void {
-        const node = blk: {
-            if (self.progress_nodes_tl) |tl|
-                break :blk tl.start(name, 0);
-
-            break :blk std.Progress.start(.{ .root_name = name });
-        };
-
-        if (become_toplevel) self.progress_nodes_tl = &node;
-
-        self.progress_nodes_mx.lock();
-        self.progress_nodes.append(self.allocator(), node) catch @panic("insufficient memory");
-        self.progress_nodes_mx.unlock();
-    }
-    pub fn progress_end(self: *Context) void {
-        self.progress_nodes_mx.lock();
-        const node = self.progress_nodes.pop() orelse return;
-        self.progress_nodes_mx.unlock();
-        node.end();
-    }
-};
-
-// TODO: make index smarter, it should know which files reference each other.
-// with this information, we could make better decisions whether to generate
-// output.
-//
-fn index(ctx: *Context) !void {
-    // avoid reindexing
-    if (ctx.sites.items.len != 0) return;
-
-    ctx.progress_start("index", true);
-    defer ctx.progress_end();
-
-    var root = std.fs.cwd().openDir(Context.params.PAGE_PATH, .{ .iterate = true }) catch |err| switch (err) {
-        error.FileNotFound => {
-            log.err("Missing {s} folder.", .{Context.params.PAGE_PATH});
-            std.os.linux.exit(1);
-        },
-        else => |e| return e,
+const Cli = struct {
+    const Defaults = struct {
+        const ROOT: []const u8 = "src/inc";
+        const SITE: []const u8 = "site";
     };
-    defer root.close();
-
-    var walk = try root.walk(ctx.allocator());
-    defer walk.deinit();
-
-    var meta: u32 = 0;
-    var htm: u32 = 0;
-    var md: u32 = 0;
-
-    // root site
-    log.debug("Indexing site root", .{});
-    var site = Context.Site{ .path = "." };
-
-    while (try walk.next()) |e| {
-        switch (e.kind) {
-            .file => {
-                ctx.progress_start(e.basename, false);
-                defer ctx.progress_end();
-
-                if (std.mem.startsWith(u8, e.basename, "meta.")) {
-                    log.debug("Indexing meta {s}", .{e.path});
-                    try site.pages.append(ctx.allocator(), .{
-                        .kind = .Meta,
-                        .name = try ctx.allocator().dupe(u8, try fs.stripExtension(e.basename)),
-                        .path = try ctx.allocator().dupe(u8, e.path),
-                    });
-                    meta += 1;
-                } else if (std.mem.endsWith(u8, e.basename, ".htm")) {
-                    log.debug("--> {s}", .{std.mem.trimEnd(u8, e.basename, ".htm")});
-                    log.debug("Indexing page {s}", .{e.path});
-                    try site.pages.append(ctx.allocator(), .{
-                        .kind = .Htm,
-                        .name = try ctx.allocator().dupe(u8, try fs.stripExtension(e.basename)),
-                        .path = try ctx.allocator().dupe(u8, e.path),
-                    });
-                    htm += 1;
-                } else if (std.mem.endsWith(u8, e.basename, ".md")) {
-                    log.debug("Indexing page {s}", .{e.path});
-                    try site.pages.append(ctx.allocator(), .{
-                        .kind = .Markdown,
-                        .name = try ctx.allocator().dupe(u8, try fs.stripExtension(e.basename)),
-                        .path = try ctx.allocator().dupe(u8, e.path),
-                    });
-                    md += 1;
-                }
-
-                // XXX: scan for refs here?
-            },
-            else => {},
-        }
-    }
-
-    try ctx.sites.append(ctx.allocator(), site);
-
-    // sort pages by name
-    for (ctx.sites.items) |s| {
-        std.mem.sort(
-            Context.Site.Page,
-            s.pages.items,
-            {},
-            struct {
-                fn lessthan(_: void, a: Context.Site.Page, b: Context.Site.Page) bool {
-                    return std.ascii.lessThanIgnoreCase(a.name, b.name);
-                }
-            }.lessthan,
-        );
-    }
-
-    log.info("Indexed {d} sites.", .{ctx.sites.items.len});
-    log.info("Indexed {d} meta pages.", .{meta});
-    log.info("Indexed {d} htm pages.", .{htm});
-    log.info("Indexed {d} markdown pages.", .{md});
-
-    return;
-}
 
-const Cli = struct {
-    fn run(ctx: *Context) !void {
-        var argv = std.process.args();
+    fn run(init: std.process.Init, ctx: *Context) !void {
+        var argv = init.minimal.args.iterate();
 
-        _ = argv.skip(); // skip arg0
+        _ = argv.skip(); // skip command name
 
-        if (argv.inner.count == 1) {
-            try Cli.mdCmd(ctx, null);
-            try Cli.genCmd(ctx, null);
+        if (argv.inner.remaining.len == 0) {
+            return Cli.helpCmd();
         }
 
         while (argv.next()) |arg| {
             if (std.mem.eql(u8, arg, "help")) {
-                Cli.helpCmd(ctx);
+                return Cli.helpCmd();
             } else if (std.mem.eql(u8, arg, "gen")) {
-                try Cli.genCmd(ctx, &argv);
+                try Cli.genCmd(init, &argv, ctx);
             } else if (std.mem.eql(u8, arg, "md")) {
                 try Cli.mdCmd(ctx, &argv);
             } else if (std.mem.eql(u8, arg, "watch")) {
-                try Cli.watchCmd(ctx);
+                try Cli.watchCmd(init, &argv);
             } else if (std.mem.eql(u8, arg, "serve")) {
-                try Cli.serveCmd(ctx);
+                try Cli.serveCmd(init, &argv);
             } else {
-                Cli.helpCmd(ctx);
-            }
-        }
-    }
-
-    fn checkOpts(maybe_argv: ?*std.process.ArgIterator, opts: []const []const u8) bool {
-        if (maybe_argv) |argv| {
-            const arg = argv.next() orelse return false;
-            // check opts
-            for (opts) |opt| {
-                if (std.mem.eql(u8, arg, opt)) return true;
+                return Cli.helpCmd();
             }
-            // rewind argv
-            argv.*.inner.index -= 1;
-            return false;
-        } else {
-            return false;
         }
     }
 
-    fn helpCmd(_: *Context) void {
-        std.debug.print("usage: rbl [gen|md|watch|serve]\n\n", .{});
+    fn helpCmd() void {
+        std.debug.print("usage: rbc COMMAND\n\n", .{});
+        std.debug.print(" rbc gen ROOT SITE [force=true]\n", .{});
+        std.debug.print(" rbc watch\n", .{});
+        std.debug.print(" rbc serve\n", .{});
+        std.debug.print("\n", .{});
     }
 
-    fn genCmd(ctx: *Context, argv: ?*std.process.ArgIterator) !void {
-        try index(ctx);
-
-        if (checkOpts(argv, &.{ "-f", "--force" }))
-            ctx.force = true;
-
-        try gen.generate(ctx);
-
-        if (checkOpts(argv, &.{ "-c", "--commit" }))
-            try exe.commit(ctx, ctx.generated.items);
+    fn genCmd(init: std.process.Init, argv: *std.process.Args.Iterator, ctx: *Context) !void {
+        const root = argv.next() orelse Cli.Defaults.ROOT;
+        const site = argv.next() orelse Cli.Defaults.SITE;
+        const force = blk: {
+            const forcearg = argv.next();
+            if (forcearg != null and std.mem.eql(u8, forcearg.?, "force=true")) {
+                break :blk true;
+            } else {
+                break :blk false;
+            }
+        };
+        log.info("root = {s}", .{root});
+        log.info("site = {s}", .{site});
+        try ctx.index(try std.Io.Dir.cwd().openDir(init.io, root, .{ .iterate = true }));
+        try ctx.render(try std.Io.Dir.cwd().createDirPathOpen(init.io, site, .{}), force);
     }
 
-    fn mdCmd(ctx: *Context, argv: ?*std.process.ArgIterator) !void {
-            try index(ctx);
-            try exe.runPandoc(ctx);
+    fn mdCmd(init: std.process.Init, argv: *std.process.Args.Iterator, ctx: *Context) !void {
+        const root = argv.next() orelse Cli.Defaults.ROOT;
+        log.info("root = {s}", .{root});
 
-            if (checkOpts(argv, &.{ "-c", "--commit" }))
-                try exe.commit(ctx, ctx.generated.items);
+        try os.shell(init.io,
+            \\pandoc --from markdown+wikilinks_title_after_pipe+autolink_bare_uris --to html5 --lua-filter preproc.lua --strip-comments --output=-
+        , .{ .environ_map = &env_map, .cwd = .{ .path = root } });
     }
 
-    fn watchCmd(ctx: *Context) !void {
-        const selfExe = try std.fs.selfExePathAlloc(ctx.allocator());
-        const command = try std.fmt.allocPrint(ctx.allocator(),
-            \\find ./src/inc/ -type f -print | entr -nc -dd -- {s} md gen
-        , .{selfExe});
+    fn watchCmd(init: std.process.Init, argv: *std.process.Args.Iterator) !void {
+        const root = argv.next() orelse Cli.Defaults.ROOT;
+        const site = argv.next() orelse Cli.Defaults.SITE;
 
-        try exe.shell(ctx, command, null);
+        const exePath = blk: {
+            var buf: [std.os.linux.PATH_MAX]u8 = undefined;
+            const n = try std.process.executablePath(init.io, &buf);
+            break :blk buf[0..n];
+        };
+        const command = try std.fmt.allocPrint(init.gpa,
+            \\find "{s}" -path '*.htm' -type f -print | entr -nc -dd -- "{s}" gen "{s}" "{s}"
+        , .{ root, exePath, root, site });
+        try os.shell(init.io, command, .{});
     }
 
-    fn serveCmd(ctx: *Context) !void {
-        // try exe.shell(ctx,
-        //     \\python -m http.server 1414
-        // , null);
+    fn serveCmd(init: std.process.Init, argv: *std.process.Args.Iterator) !void {
+        const root = argv.next() orelse ".";
+        log.info("root = {s}", .{root});
 
-        var env_map = try std.process.getEnvMap(ctx.allocator());
+        var env_map = try init.minimal.environ.createMap(init.gpa);
         defer env_map.deinit();
-
         try env_map.put("CADDY_HTTP_PORT", "1414");
         try env_map.put("CADDY_HOST", "localhost");
 
-        try exe.shell(ctx,
-            \\caddy fmt --overwrite && caddy run --watch
-        , &env_map);
+        if ((try std.Io.Dir.cwd().openDir(init.io, root, .{})).access(init.io, "Caddyfile", .{})) {
+            try os.shell(init.io,
+                \\caddy fmt --overwrite && caddy run --watch
+            , .{ .environ_map = &env_map, .cwd = .{ .path = root } });
+        } else |_| {
+            try os.shell(init.io,
+                \\caddy file-server --listen=$CADDY_HOST:$CADDY_HTTP_PORT
+            , .{ .environ_map = &env_map, .cwd = .{ .path = root } });
+        }
     }
 };
 
-pub fn main() !void {
-    var gpa = std.heap.GeneralPurposeAllocator(.{}).init;
-    const allocator = gpa.allocator();
-    defer _ = gpa.deinit();
-
-    var ctx = try Context.create(allocator);
-    defer ctx.destroy();
+pub fn main(init: std.process.Init) !void {
+    var ctx = try Context.init(init.io, init.gpa);
+    defer ctx.deinit();
 
-    try Cli.run(ctx);
+    try Cli.run(init, &ctx);
 }