// 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 Context = @import("Context.zig"); const os = @import("os.zig"); const std = @import("std"); const log = std.log.scoped(.rbc); // tune std.log options pub const std_options = std.Options{ .log_level = if (@import("builtin").mode == .Debug) .debug else .info, }; const Cli = struct { const Defaults = struct { const ROOT: []const u8 = "src/inc"; const SITE: []const u8 = "site"; }; fn run(init: std.process.Init, ctx: *Context) !void { var argv = init.minimal.args.iterate(); _ = argv.skip(); // skip command name if (argv.inner.remaining.len == 0) { return Cli.helpCmd(); } while (argv.next()) |arg| { if (std.mem.eql(u8, arg, "help")) { return Cli.helpCmd(); } else if (std.mem.eql(u8, arg, "gen")) { 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(init, &argv); } else if (std.mem.eql(u8, arg, "serve")) { try Cli.serveCmd(init, &argv); } else { return Cli.helpCmd(); } } } 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(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(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}); 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(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; 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(init: std.process.Init, argv: *std.process.Args.Iterator) !void { const root = argv.next() orelse "."; log.info("root = {s}", .{root}); 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"); 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(init: std.process.Init) !void { var ctx = try Context.init(init.io, init.gpa); defer ctx.deinit(); try Cli.run(init, &ctx); }