diff options
Diffstat (limited to 'src/main.zig')
| -rw-r--r-- | src/main.zig | 332 |
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); } |