2026-01-24 23:53:19 +05:30
|
|
|
// ViewEngine - Simple template engine for web servers
|
|
|
|
|
//
|
|
|
|
|
// Provides a high-level API for rendering Pug templates from a views directory.
|
|
|
|
|
// Works with any web server that provides an allocator (httpz, zap, etc).
|
|
|
|
|
//
|
|
|
|
|
// Usage:
|
|
|
|
|
// const engine = ViewEngine.init(.{ .views_dir = "views" });
|
|
|
|
|
// const html = try engine.render(allocator, "pages/home", .{ .title = "Home" });
|
2026-01-17 18:50:16 +05:30
|
|
|
|
|
|
|
|
const std = @import("std");
|
2026-01-24 23:53:19 +05:30
|
|
|
const pug = @import("pug.zig");
|
2026-01-17 18:50:16 +05:30
|
|
|
|
|
|
|
|
pub const Options = struct {
|
2026-01-24 23:53:19 +05:30
|
|
|
/// Root directory containing view templates
|
|
|
|
|
views_dir: []const u8 = "views",
|
|
|
|
|
/// File extension for templates
|
2026-01-17 18:50:16 +05:30
|
|
|
extension: []const u8 = ".pug",
|
2026-01-24 23:53:19 +05:30
|
|
|
/// Enable pretty-printing with indentation
|
2026-01-17 18:50:16 +05:30
|
|
|
pretty: bool = true,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub const ViewEngine = struct {
|
|
|
|
|
options: Options,
|
|
|
|
|
|
2026-01-17 23:59:22 +05:30
|
|
|
pub fn init(options: Options) ViewEngine {
|
2026-01-24 23:53:19 +05:30
|
|
|
return .{ .options = options };
|
2026-01-17 18:50:16 +05:30
|
|
|
}
|
|
|
|
|
|
2026-01-17 23:59:22 +05:30
|
|
|
/// Renders a template file with the given data context.
|
2026-01-24 23:53:19 +05:30
|
|
|
/// Template path is relative to views_dir, extension added automatically.
|
|
|
|
|
pub fn render(self: *const ViewEngine, allocator: std.mem.Allocator, template_path: []const u8, data: anytype) ![]const u8 {
|
|
|
|
|
_ = data; // TODO: pass data to template
|
|
|
|
|
|
2026-01-17 18:50:16 +05:30
|
|
|
// Build full path
|
|
|
|
|
const full_path = try self.resolvePath(allocator, template_path);
|
|
|
|
|
defer allocator.free(full_path);
|
|
|
|
|
|
2026-01-24 23:53:19 +05:30
|
|
|
// Compile the template
|
|
|
|
|
var result = pug.compileFile(allocator, full_path, .{
|
|
|
|
|
.pretty = self.options.pretty,
|
|
|
|
|
.filename = full_path,
|
|
|
|
|
}) catch |err| {
|
|
|
|
|
return err;
|
2026-01-17 18:50:16 +05:30
|
|
|
};
|
2026-01-17 23:59:22 +05:30
|
|
|
|
2026-01-24 23:53:19 +05:30
|
|
|
if (result.err) |*e| {
|
|
|
|
|
e.deinit();
|
|
|
|
|
return error.ParseError;
|
2026-01-17 18:50:16 +05:30
|
|
|
}
|
|
|
|
|
|
2026-01-24 23:53:19 +05:30
|
|
|
return result.html;
|
2026-01-17 18:50:16 +05:30
|
|
|
}
|
|
|
|
|
|
2026-01-24 23:53:19 +05:30
|
|
|
/// Resolves a template path relative to views directory
|
2026-01-17 23:59:22 +05:30
|
|
|
fn resolvePath(self: *const ViewEngine, allocator: std.mem.Allocator, template_path: []const u8) ![]const u8 {
|
2026-01-17 18:50:16 +05:30
|
|
|
// Add extension if not present
|
|
|
|
|
const with_ext = if (std.mem.endsWith(u8, template_path, self.options.extension))
|
|
|
|
|
try allocator.dupe(u8, template_path)
|
|
|
|
|
else
|
|
|
|
|
try std.fmt.allocPrint(allocator, "{s}{s}", .{ template_path, self.options.extension });
|
|
|
|
|
defer allocator.free(with_ext);
|
|
|
|
|
|
2026-01-17 23:59:22 +05:30
|
|
|
return std.fs.path.join(allocator, &.{ self.options.views_dir, with_ext });
|
2026-01-17 18:50:16 +05:30
|
|
|
}
|
2026-01-22 11:10:47 +05:30
|
|
|
};
|