diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/exe.zig | 21 | ||||
| -rw-r--r-- | src/fs.zig | 1 | ||||
| -rw-r--r-- | src/gen.zig | 127 | ||||
| -rw-r--r-- | src/main.zig | 146 |
4 files changed, 176 insertions, 119 deletions
diff --git a/src/exe.zig b/src/exe.zig index 998ea3b..ad3a7aa 100644 --- a/src/exe.zig +++ b/src/exe.zig @@ -57,20 +57,29 @@ pub fn WaitPool( pub fn runPandoc(ctx: *Context) !void { ctx.generated.clearRetainingCapacity(); - ctx.progress_start("Processing markdown files"); + ctx.progress_start("Processing markdown files", true); defer ctx.progress_end(); - // TODO: check if pandoc is installed and exit gracefully + var md = try std.ArrayList([]const u8).initCapacity(ctx.allocator(), 1); + defer md.deinit(ctx.allocator()); + + // TODO: support more than one site + for (ctx.sites.getLast().pages.items) |p| + if (p.kind == .Markdown) try md.append(ctx.allocator(), p.path); + // TODO: check if pandoc is installed and exit gracefully try WaitPool( []const u8, pandocWorker, - ).work(ctx, ctx.markdown.items, .{}); + ).work(ctx, md.items, .{}); - log.info("Processed {d} markdown files.", .{ctx.markdown.items.len}); + log.info("Processed {d} markdown files.", .{md.items.len}); } fn pandocWorker(ctx: *Context, page: []const u8) !void { + ctx.progress_start(page, true); + defer ctx.progress_end(); + const source_path = try std.fs.path.join(ctx.allocator(), &.{ Context.params.PAGE_PATH, page }); defer ctx.allocator().free(source_path); const target_path = try fs.replaceExtension(ctx.allocator(), source_path, "htm"); @@ -159,7 +168,7 @@ pub fn captureStdout(ctx: *Context, argv: []const []const u8, env_map: ?*std.pro const label = try std.fmt.allocPrint(ctx.allocator(), "running {s}", .{command}); defer ctx.allocator().free(label); - ctx.progress_start(label); + ctx.progress_start(label, true); defer ctx.progress_end(); log.debug("running {s}", .{command}); @@ -187,7 +196,7 @@ pub fn shell(ctx: *Context, command: []const u8, env_map: ?*std.process.EnvMap) const label = try std.fmt.allocPrint(ctx.allocator(), "running {s}", .{command}); defer ctx.allocator().free(label); - ctx.progress_start(label); + ctx.progress_start(label, true); defer ctx.progress_end(); var p = std.process.Child.init(&.{ diff --git a/src/fs.zig b/src/fs.zig index 08ad7ee..f139653 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -124,7 +124,6 @@ pub fn joinPathAndFilename(alloc: std.mem.Allocator, paths: []const []const u8, const filename = try std.mem.concat(fba.allocator(), u8, filename_parts); var p = try std.ArrayList([]const u8).initCapacity(fba.allocator(), paths.len + 1); - p.appendSliceAssumeCapacity(paths); p.appendAssumeCapacity(filename); diff --git a/src/gen.zig b/src/gen.zig index 449342e..fd1dff7 100644 --- a/src/gen.zig +++ b/src/gen.zig @@ -9,21 +9,21 @@ const log = std.log.scoped(.gen); pub fn generate(ctx: *Context) !void { ctx.generated.clearRetainingCapacity(); - ctx.progress_start("generate"); + ctx.progress_start("generate", true); defer ctx.progress_end(); - try genIndex(ctx); + try index(ctx); log.info("Generated index.", .{}); - try genSites(ctx); + try pages(ctx); log.info("Generated {d} files.", .{ctx.generated.items.len}); log.info("Generated {d} tildes.", .{ctx.tilde}); } -fn genIndex(ctx: *Context) !void { - ctx.progress_start("index.htm"); +fn index(ctx: *Context) !void { + ctx.progress_start("index.htm", false); defer ctx.progress_end(); const srcpathIndex = try fs.joinPathAndFilename(ctx.allocator(), &.{Context.params.PAGE_PATH}, &.{"index.htm"}); @@ -40,32 +40,21 @@ fn genIndex(ctx: *Context) !void { \\ ); - // alphanum ascii sort - std.mem.sort( - []const u8, - ctx.pages.items, - {}, - struct { - fn lessthan(_: void, a: []const u8, b: []const u8) bool { - return std.ascii.lessThanIgnoreCase(a, b); + for (ctx.sites.items) |s| { + for (s.pages.items) |p| { + if (p.kind == .Htm) { + const dstpath = try fs.joinPathAndFilename(ctx.allocator(), &.{Context.params.SITE_PATH}, &.{ p.name, ".html" }); + defer ctx.allocator().free(dstpath); + + try w.interface.print( + \\<li><a href="/{[LINK]s}">{[NAME]s}</a></li> + \\ + , .{ + .LINK = dstpath, + .NAME = p.name, + }); } - }.lessthan, - ); - - for (ctx.pages.items) |p| { - if (std.mem.startsWith(u8, p, "meta.")) continue; - - const dstname = try fs.stripExtension(p); - const dstpath = try fs.joinPathAndFilename(ctx.allocator(), &.{Context.params.SITE_PATH}, &.{ dstname, ".html" }); - defer ctx.allocator().free(dstpath); - - try w.interface.print( - \\<li><a href="/{[LINK]s}">{[NAME]s}</a></li> - \\ - , .{ - .LINK = dstpath, - .NAME = dstname, - }); + } } try w.interface.writeAll( @@ -76,27 +65,25 @@ fn genIndex(ctx: *Context) !void { try w.interface.flush(); } -fn genSites(ctx: *Context) !void { - for (ctx.pages.items) |p| { - if (std.mem.startsWith(u8, std.fs.path.basename(p), "meta.")) { - // meta pages - try meta(ctx, p); - continue; +fn pages(ctx: *Context) !void { + for (ctx.sites.items) |s| { + for (s.pages.items) |p| { + if (p.kind == .Meta) { + try meta(ctx, p); + } else if (p.kind == .Htm) { + try page(ctx, p); + } } - - // regular pages - try genSite(ctx, p); } } -fn genSite(ctx: *Context, p: []const u8) !void { - ctx.progress_start(std.fs.path.basename(p)); +fn page(ctx: *Context, p: Context.Site.Page) !void { + ctx.progress_start(p.name, false); defer ctx.progress_end(); - const dstname = try fs.stripExtension(p); - const srcpath = try fs.joinPathAndFilename(ctx.allocator(), &.{Context.params.PAGE_PATH}, &.{ dstname, ".htm" }); + const srcpath = try fs.joinPathAndFilename(ctx.allocator(), &.{Context.params.PAGE_PATH}, &.{ p.name, ".htm" }); defer ctx.allocator().free(srcpath); - const dstpath = try fs.joinPathAndFilename(ctx.allocator(), &.{Context.params.SITE_PATH}, &.{ dstname, ".html" }); + const dstpath = try fs.joinPathAndFilename(ctx.allocator(), &.{Context.params.SITE_PATH}, &.{ p.name, ".html" }); defer ctx.allocator().free(dstpath); const rel_dstpath = try std.fs.path.relative(ctx.allocator(), Context.params.SITE_PATH, dstpath); defer ctx.allocator().free(rel_dstpath); @@ -112,14 +99,14 @@ fn genSite(ctx: *Context, p: []const u8) !void { const dstmtime = try fs.getLastEdit(dstpath); if (!ctx.force and srcmtime.sec < dstmtime.sec) return; - log.debug("generating {s} at {s}", .{ p, rel_dstpath }); + log.debug("generating {s} at {s}", .{ p.path, rel_dstpath }); const f = try fs.FileSwapper.createAt(ctx.allocator(), dstpath); var buffer: [1024]u8 = undefined; var w = f.writer(&buffer); - const page_name = try std.mem.replaceOwned(u8, ctx.allocator(), dstname, "_", " "); + const page_name = try std.mem.replaceOwned(u8, ctx.allocator(), p.name, "_", " "); defer ctx.allocator().free(page_name); try build(ctx.allocator(), &w.interface, page_name, srcpath); @@ -230,19 +217,18 @@ fn build(allocator: std.mem.Allocator, w: *std.Io.Writer, page_name: []const u8, // build a virtual html file, currently either: // - meta.nav: used for populating the global navigation menu // - meta.tilde: used to create ~foobar files -fn meta(ctx: *Context, p: []const u8) !void { - ctx.progress_start(std.fs.path.basename(p)); +fn meta(ctx: *Context, p: Context.Site.Page) !void { + ctx.progress_start(p.name, false); defer ctx.progress_end(); ctx.meta += 1; - if (std.mem.startsWith(u8, p, "meta.nav.")) return; + if (std.mem.startsWith(u8, p.name, "meta.nav")) return; - if (std.mem.startsWith(u8, p, "meta.tilde.")) { - const dot = std.mem.indexOfPosLinear(u8, p, 11, "."); - const dstname = p[11..dot.?]; - const srcpath = try fs.joinPathAndFilename(ctx.allocator(), &.{Context.params.PAGE_PATH}, &.{p}); + if (std.mem.startsWith(u8, p.name, "meta.tilde")) { + const srcpath = try fs.joinPathAndFilename(ctx.allocator(), &.{Context.params.PAGE_PATH}, &.{p.path}); defer ctx.allocator().free(srcpath); + const dstname = p.name[11..]; // trim "meta.tilde." from the name const dstpath = try fs.joinPathAndFilename(ctx.allocator(), &.{Context.params.LINK_PATH}, &.{ "~", dstname }); defer ctx.allocator().free(dstpath); @@ -289,6 +275,7 @@ fn meta(ctx: *Context, p: []const u8) !void { return; } + log.err("unknown meta page type: {s}", .{p.name}); return error.unknownMeta; } @@ -390,49 +377,49 @@ const Page = struct { }; fn portal(allocator: std.mem.Allocator, w: *std.Io.Writer, refname: []const u8, insert_heading: bool) anyerror!void { - const page = try Page.from_ref(allocator, refname); - defer page.deinit(); + const p = try Page.from_ref(allocator, refname); + defer p.deinit(); if (insert_heading) try w.print( \\<h2 id="{[ID]s}"><a href="/{[PATH]s}">{[NAME]s}</a></2> \\ , .{ - .ID = page.ref, - .NAME = page.name, - .PATH = page.html, + .ID = p.ref, + .NAME = p.name, + .PATH = p.html, }); - try inject(allocator, w, page.htm); + try inject(allocator, w, p.htm); // XXX: track ref count? } fn ref(allocator: std.mem.Allocator, w: *std.Io.Writer, refname: []const u8) anyerror!void { - const page = try Page.from_ref(allocator, refname); - defer page.deinit(); + const p = try Page.from_ref(allocator, refname); + defer p.deinit(); try w.print( \\<a href="/{[PATH]s}{[ANCHOR]s}">{[NAME]s}</a> , .{ - .NAME = page.name, - .PATH = page.html, - .ANCHOR = page.anchor, + .NAME = p.name, + .PATH = p.html, + .ANCHOR = p.anchor, }); // XXX: track ref count? } fn ref_link(allocator: std.mem.Allocator, w: *std.Io.Writer, refname: []const u8) anyerror!void { - const page = try Page.from_ref(allocator, refname); - defer page.deinit(); + const p = try Page.from_ref(allocator, refname); + defer p.deinit(); - try w.print("/{s}{s}", .{ page.html, page.anchor }); + try w.print("/{s}{s}", .{ p.html, p.anchor }); } fn ref_name(allocator: std.mem.Allocator, w: *std.Io.Writer, refname: []const u8) anyerror!void { - const page = try Page.from_ref(allocator, refname); - defer page.deinit(); + const p = try Page.from_ref(allocator, refname); + defer p.deinit(); - try w.print("{s}", .{page.ref}); + try w.print("{s}", .{p.ref}); } diff --git a/src/main.zig b/src/main.zig index 3edfb1c..fb8e687 100644 --- a/src/main.zig +++ b/src/main.zig @@ -7,6 +7,7 @@ const exe = @import("exe.zig"); const gen = @import("gen.zig"); +const fs = @import("fs.zig"); const std = @import("std"); @@ -14,20 +15,34 @@ const log = std.log.scoped(.rbc); // tune std.log options pub const std_options = std.Options{ - .log_level = .info, + .log_level = .debug, }; 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, - pages: std.ArrayListUnmanaged([]const u8) = .{}, - markdown: std.ArrayListUnmanaged([]const u8) = .{}, - + sites: std.ArrayListUnmanaged(Site) = .{}, generated: std.ArrayListUnmanaged([]const u8) = .{}, meta: usize = 0, tilde: usize = 0, @@ -62,11 +77,6 @@ pub const Context = struct { .child_allocator = self.arena.allocator(), .mutex = self.arena_mx, }; - // create root progress node - try self.progress_nodes.append( - self.allocator(), - std.Progress.start(.{ .root_name = "" }), - ); return self; } @@ -82,17 +92,25 @@ pub const Context = struct { } // progress handling - pub fn progress_start(self: *Context, name: []const u8) void { + 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(); - const new = self.progress_nodes.getLast().start(name, 0); - self.progress_nodes.append(self.allocator(), new) catch @panic("insufficient memory"); + 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 current = self.progress_nodes.pop() orelse return; + const node = self.progress_nodes.pop() orelse return; self.progress_nodes_mx.unlock(); - current.end(); + node.end(); } }; @@ -102,14 +120,14 @@ pub const Context = struct { // fn index(ctx: *Context) !void { // avoid reindexing - if ((ctx.pages.items.len != 0) and (ctx.markdown.items.len != 0)) return; + if (ctx.sites.items.len != 0) return; - ctx.progress_start("index"); + 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 => { - std.log.err("Missing {s} folder.", .{Context.params.PAGE_PATH}); + log.err("Missing {s} folder.", .{Context.params.PAGE_PATH}); std.os.linux.exit(1); }, else => |e| return e, @@ -119,19 +137,45 @@ fn index(ctx: *Context) !void { 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); + ctx.progress_start(e.basename, false); defer ctx.progress_end(); - if (std.mem.endsWith(u8, e.basename, ".htm")) { - std.log.debug("Indexing {s}", .{e.path}); - try ctx.pages.append(ctx.allocator(), try ctx.allocator().dupe(u8, e.path)); - } - if (std.mem.endsWith(u8, e.basename, ".md")) { - std.log.debug("Indexing {s}", .{e.path}); - try ctx.markdown.append(ctx.allocator(), try ctx.allocator().dupe(u8, e.path)); + 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? @@ -140,8 +184,26 @@ fn index(ctx: *Context) !void { } } - log.info("Indexed {d} pages.", .{ctx.pages.items.len}); - log.info("Indexed {d} markdown files.", .{ctx.markdown.items.len}); + 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; } @@ -206,11 +268,11 @@ const Cli = struct { } fn mdCmd(ctx: *Context, argv: ?*std.process.ArgIterator) !void { - try index(ctx); - try exe.runPandoc(ctx); + try index(ctx); + try exe.runPandoc(ctx); - if (checkOpts(argv, &.{ "-c", "--commit" })) - try exe.commit(ctx, ctx.generated.items); + if (checkOpts(argv, &.{ "-c", "--commit" })) + try exe.commit(ctx, ctx.generated.items); } fn watchCmd(ctx: *Context) !void { @@ -223,19 +285,19 @@ const Cli = struct { } fn serveCmd(ctx: *Context) !void { - try exe.shell(ctx, - \\python -m http.server 1414 - , null); + // try exe.shell(ctx, + // \\python -m http.server 1414 + // , null); - // var env_map = try std.process.getEnvMap(ctx.allocator()); - // defer env_map.deinit(); + var env_map = try std.process.getEnvMap(ctx.allocator()); + 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); + 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); } }; |