srctree

Gregory Mullen parent f7bb68a3 7297fcc3
add AutoTranslate to templates

inlinesplit
examples/template.zig added: 90, removed: 7, total 83
@@ -32,6 +32,12 @@ fn index(frame: *verse.Frame) Router.Error!void {
.{ .color = "red", .text = "This color is red" },
.{ .color = "blue", .text = "This color is blue" },
.{ .color = "green", .text = "This color is green" },
// The template system also provides a translation method if you
// have an existing source struct you'd like to use.
.translate(BasicLoopSourceObject{ .color = "purple", .text = "This color is purple" }),
// You can't provide an incompatible type trying to do so is a
// compile error.
//.translate(BasicLoopIncomplete{ .text = "The color field is missing!" }),
},
 
.slices = &.{
@@ -52,9 +58,14 @@ fn index(frame: *verse.Frame) Router.Error!void {
try frame.sendPage(&page);
}
 
const routes = Router.Routes(&[_]Router.Match{
Router.GET("", index),
});
const BasicLoopSourceObject = struct {
color: []const u8,
text: []const u8,
};
 
const BasicLoopIncomplete = struct {
text: []const u8,
};
 
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
@@ -70,8 +81,12 @@ pub fn main() !void {
std.posix.exit(1);
};
}
const std = @import("std");
 
const routes = Router.Routes(&[_]Router.Match{
Router.GET("", index),
});
 
const std = @import("std");
const verse = @import("verse");
const PageData = verse.template.PageData;
const Router = verse.Router;
 
src/template/struct-emit.zig added: 90, removed: 7, total 83
@@ -74,6 +74,13 @@ const AbstTree = struct {
for (self.children) |child| {
try w.print("{}", .{child});
}
try w.writeAll(
\\
\\ pub const Translate = AutoTranslate(@This());
\\ pub const translate = Translate.translate;
\\ pub const translateAlloc = Translate.translateAlloc;
\\
);
try w.writeAll("};\n");
} else {
comptime unreachable;
@@ -81,6 +88,36 @@ const AbstTree = struct {
}
};
 
fn AutoTranslate(into: type) type {
return struct {
pub const Self = @This();
pub const To: type = into;
pub const fields = @typeInfo(To).@"struct".fields;
 
pub fn translate(from: anytype) To {
const From = @TypeOf(from);
var result: To = undefined;
inline for (Self.fields) |field| {
if (!@hasField(From, field.name)) {
@compileError("Source struct " ++
@typeName(From) ++
" is missing required field '" ++ field.name ++
"' while translating into " ++
@typeName(To));
}
@field(result, field.name) = @field(from, field.name);
}
return result;
}
 
pub fn translateAlloc(a: std.mem.Allocator, from: anytype) !To {
_ = a;
_ = from;
comptime unreachable;
}
};
}
 
var root_tree: std.StringHashMapUnmanaged(*AbstTree) = .{};
 
pub fn main() !void {
@@ -98,8 +135,39 @@ pub fn main() !void {
var wfile = try wout_dir.createFile(std.fs.path.basename(wout_path.?), .{});
defer wfile.close();
try wfile.writeAll(
\\// Generated by srctree template compiler
\\//! Generated by srctree template compiler
\\
\\const std = @import("std");
\\
\\fn AutoTranslate(into: type) type {
\\ return struct {
\\ pub const Self = @This();
\\ pub const To: type = into;
\\ pub const fields = @typeInfo(To).@"struct".fields;
\\
\\ pub fn translate(from: anytype) To {
\\ const From = @TypeOf(from);
\\ var result: To = undefined;
\\ inline for (Self.fields) |field| {
\\ if (!@hasField(From, field.name)) {
\\ @compileError("Source struct " ++
\\ @typeName(From) ++
\\ " is missing required field '" ++ field.name ++
\\ "' while translating into " ++
\\ @typeName(To));
\\ }
\\ @field(result, field.name) = @field(from, field.name);
\\ }
\\ return result;
\\ }
\\
\\ pub fn translateAlloc(a: std.mem.Allocator, from: anytype) !To {
\\ _ = a;
\\ _ = from;
\\ comptime unreachable;
\\ }
\\ };
\\}
);
var wout = wfile.writer();