const std = @import("std"); pub fn File(comptime tag: ?[]const u8, Iterator: type) type { const opening = if (tag) |t| "<" ++ t ++ ">" else ""; const closing = if (tag) |t| "" else ""; return struct { file: []const u8, do_minify: bool, pub fn path(comptime p: []const u8) @This() { return .{ .file = p, .do_minify = false, }; } pub fn minify(comptime p: []const u8) @This() { return .{ .file = p, .do_minify = true, }; } fn content(comptime self: @This()) []const u8 { return opening ++ @embedFile(self.file) ++ closing; } fn content_minified(comptime self: @This()) []const u8 { var minified: []const u8 = ""; var iterator: Iterator = .init(@embedFile(self.file)); while (iterator.next()) |segment| { minified = minified ++ segment; } return opening ++ minified ++ closing; } pub fn to_html_string(comptime self: @This()) []const u8 { return if (self.do_minify) self.content_minified() else self.content(); } }; } fn StringIterator(comptime quotes: []const u8) type { return struct { content: []const u8, pub fn init(content: []const u8) @This() { return .{ .content = content }; } pub fn next(self: *@This()) ?[]const u8 { @setEvalBranchQuota(self.content.len * 20); self.content = std.mem.trimStart(u8, self.content, &std.ascii.whitespace); if (self.content.len == 0) return null; var index: usize = 0; while (self.content.len > index) { const char = self.content[index]; if (std.mem.indexOfScalar(u8, quotes, char) != null) { index += 1; while (self.content.len > index) { if (self.content[index] == char) { index += 1; break; } index += 1; } } else if (std.ascii.isWhitespace(char)) { break; } index += 1; } const block = self.content[0..index]; self.content = self.content[index..]; return block ++ " "; } }; } pub const Style = File("style", StringIterator("\"'")); pub const Script = File("script", StringIterator("\"'`")); pub const Html = File(null, StringIterator("\"'"));