diff options
| author | Robert Günzler <r@gnzler.io> | 2026-09-18 04:00:18 +0200 |
|---|---|---|
| committer | Robert Günzler <r@gnzler.io> | 2026-09-22 22:37:51 +0200 |
| commit | 5bc242742e1225c7c3b87a31c2c2a3998ac18685 (patch) | |
| tree | baae923037b49ba31bb0a15ece2eec4bea13ab25 /src/gen.zig | |
| parent | c677e8873e0eae790a1b8b07844a10efa9f63a1e (diff) | |
| download | site-5bc242742e1225c7c3b87a31c2c2a3998ac18685.tar.gz | |
v2
Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to '')
| -rw-r--r-- | src/gen.zig | 255 |
1 files changed, 0 insertions, 255 deletions
diff --git a/src/gen.zig b/src/gen.zig deleted file mode 100644 index 8def5cf..0000000 --- a/src/gen.zig +++ /dev/null @@ -1,255 +0,0 @@ -const std = @import("std"); - -const os = @import("os.zig"); - -const log = std.log.scoped(.gen); - -const DELIM_START: []const u8 = "{["; -const DELIM_END: []const u8 = "]}"; - -pub const BuildOpts = struct { - root_dir: std.Io.Dir, - site_dir: std.Io.Dir, - - page_path: []const u8, - page_name: []const u8, - - meta: struct { - source_url: []const u8, - site_name: []const u8, - license_url: []const u8, - license_holder: []const u8, - license_name: []const u8, - license_year: []const u8, - public_inbox: []const u8, - }, -}; - -// build a single html file -pub fn build_htm(io: std.Io, gpa: std.mem.Allocator, w: *std.Io.Writer, opts: BuildOpts) !void { - const stat = try opts.root_dir.statFile(io, opts.page_path, .{}); - const mtime_str = try os.formatTimestamp(gpa, stat.mtime, "%F"); - defer gpa.free(mtime_str); - - // HEAD - try w.print( - \\<!-- Generated file, do not edit, you may edit and discard {[SOURCE]s} :) --> - \\<!DOCTYPE html> - \\<html lang="en"> - \\<head> - \\<meta charset="utf-8"> - \\<meta name="thumbnail" content="/media/services/thumbnail.jpg"> - \\<meta name="viewport" content="width=device-width,initial-scale=1"> - \\<link rel="preload" href="/media/interface/logo.svg" as="image"> - \\<link rel="alternate" type="application/rss+xml" title="RSS Feed" href="/links/rss.xml"> - \\<link rel="stylesheet" type="text/css" href="/links/main.css"> - \\<link rel="shortcut icon" type="image/png" href="/media/services/icon.png"> - \\<link rel="license" href="{[LICENSE_URL]s}"> - \\<title>{[SITE_NAME]s} — {[PAGE_NAME]s}</title> - \\</head> - \\<body> - \\<header> - \\<a href="/"><img src="/media/interface/logo.svg" alt="{[SITE_NAME]s}"></a> - \\ - , .{ - .SOURCE = opts.page_path, - .PAGE_NAME = opts.page_name, - .LICENSE_URL = opts.meta.license_url, - .SITE_NAME = opts.meta.site_name, - }); - - // NAV - try w.writeAll( - \\<nav> - ); - template_portal(io, gpa, w, "meta.nav", opts) catch |err| { - if (err != error.FileNotFound) return err; - }; - try w.writeAll( - \\</nav> - \\</header> - \\ - ); - - // MAIN - try w.writeAll( - \\<main> - \\ - ); - try inject(io, gpa, w, opts); - try w.writeAll( - \\</main> - \\ - ); - - // FOOTER - try w.print( - \\<footer> - \\Edited on {[MTIME]s} <a href="{[SOURCE_URL]s}/{[PATH]s}" target="_blank">[src]</a> - \\<a href="mailto:{[PUBLIC_INBOX]s}">[comments]</a> - \\<br> - \\<b>{[LICENSE_HOLDER]s}</b> - \\© {[LICENSE_YEAR]s} - \\— - \\<a href="{[LICENSE_URL]s}" target="_blank">{[LICENSE_NAME]s}</a> - \\</footer> - \\ - , .{ - .MTIME = mtime_str, - .PATH = opts.page_path, - .LICENSE_HOLDER = opts.meta.license_holder, - .LICENSE_NAME = opts.meta.license_name, - .LICENSE_URL = opts.meta.license_url, - .LICENSE_YEAR = opts.meta.license_year, - .PUBLIC_INBOX = opts.meta.public_inbox, - .SOURCE_URL = opts.meta.source_url, - }); - - try w.writeAll( - \\<a href="#top">[⏶]</a> - \\ - ); - - try w.writeAll( - \\</body> - \\</html> - ); - - try w.flush(); -} - -// parses srcpath, resolves refs and portals and writes everything to w -fn inject(io: std.Io, gpa: std.mem.Allocator, w: *std.Io.Writer, opts: BuildOpts) !void { - var arena = std.heap.ArenaAllocator.init(gpa); - defer arena.deinit(); - - const inc = opts.root_dir.openFile(io, opts.page_path, .{ .mode = .read_only }) catch |err| { - if (err == error.FileNotFound) - log.warn("Missing {s} include.", .{opts.page_path}); - - return err; - }; - defer inc.close(io); - - const buf = try arena.allocator().alloc(u8, 1024); - var r = inc.readerStreaming(io, buf); - - var chunkw = std.Io.Writer.Allocating.init(arena.allocator()); - defer chunkw.deinit(); - - while (true) { - defer chunkw.clearRetainingCapacity(); // reset chunk - - _ = r.interface.streamDelimiter(&chunkw.writer, '\n') catch |err| - if (err == error.EndOfStream) break else return err; - - _ = r.interface.toss(1); // skip newline delimiter - - var chunk = chunkw.written(); - templating: while (true) { - if (std.mem.indexOfPosLinear(u8, chunk, 0, DELIM_START)) |start| { - if (std.mem.indexOfPosLinear(u8, chunk, start + 1, DELIM_END)) |end| { - try w.writeAll(chunk[0..start]); - template(io, gpa, w, chunk[(start + DELIM_START.len)..end], opts) catch |err| switch (err) { - error.FileNotFound => { - // log.warn("ref {s} not found", .{chunk[start + 1 .. end]}); - // trim already written chunk and exit templating - chunk = chunk[start..]; - break :templating; - }, - else => @panic("Failed to resolve template"), - }; - chunk = chunk[end + DELIM_END.len ..]; - } else break :templating; - } else break :templating; - } - - try w.writeAll(chunk); - try w.writeByte('\n'); - try w.flush(); - } - - try w.flush(); -} - -// implements various reference types that can be used in .htm templates -fn template(io: std.Io, gpa: std.mem.Allocator, w: *std.Io.Writer, ref_name: []const u8, opts: BuildOpts) anyerror!void { - switch (ref_name[0]) { - '/' => return try template_portal(io, gpa, w, ref_name[1..], opts), - // '$' => return try ref_link(allocator, w, refname[1..]), - // '+' => return try ref_name(allocator, w, refname[1..]), - else => return try template_ref(io, gpa, w, ref_name, opts), - } -} - -// insert link -fn template_ref(io: std.Io, gpa: std.mem.Allocator, w: *std.Io.Writer, ref_name: []const u8, opts: BuildOpts) anyerror!void { - log.debug("template:ref (ref={s})", .{ref_name}); - - const ref_path = try std.fmt.allocPrint(gpa, "{s}.html", .{ref_name}); - defer gpa.free(ref_path); - - _ = io; - _ = opts; - - try w.print( - \\<a href="{s}">{s}</a> - , .{ ref_path, ref_name }); -} - -// insert contents -fn template_portal(io: std.Io, gpa: std.mem.Allocator, w: *std.Io.Writer, ref_name: []const u8, opts: BuildOpts) anyerror!void { - log.debug("template:portal (ref={s})", .{ref_name}); - - const ref_path = try std.fmt.allocPrint(gpa, "{s}.htm", .{ref_name}); - defer gpa.free(ref_path); - - try opts.root_dir.access(io, ref_path, .{}); - - var iopts = opts; - iopts.page_path = ref_path; - iopts.page_name = ref_name; - try inject(io, gpa, w, iopts); -} - -// build a meta file -pub fn build_meta(io: std.Io, gpa: std.mem.Allocator, w: *std.Io.Writer, opts: BuildOpts) !void { - if (std.mem.startsWith(u8, opts.page_name, "meta.nav")) return; - - if (std.mem.startsWith(u8, opts.page_name, "meta.tilde")) { - const name = std.mem.cutPrefix(u8, opts.page_name, "meta.tilde.").?; - const path = try std.fmt.allocPrint(gpa, "~{s}", .{name}); - defer gpa.free(path); - - const link = blk: { - const content = try opts.root_dir.readFileAlloc(io, opts.page_path, gpa, .unlimited); - break :blk std.mem.trim(u8, content, " \n\t\r"); - }; - defer gpa.free(link); - - try w.interface.print( - \\<!DOCTYPE html> - \\<html lang="en"> - \\<head> - \\<meta charset="utf-8"> - \\<meta http-equiv="refresh" content="0; url={[LINK]s}"> - \\<meta name="fediverse:creator" content="@robertgzr@toot.berlin"> - \\<link rel="stylesheet" type="text/css" href="links/main.css"> - \\</head> - \\<body> - \\<noscript>Continue to <a href="{[LINK]s}">{[LINK]s}</a>.</noscript> - \\<script> - \\'use strict' - \\window.location.replace("{[LINK]s}") - \\</script> - \\</body> - \\</html> - \\ - , .{ - .LINK = link, - }); - try w.interface.flush(); - } - - return error.UnsupportedMeta; -} |