srctree

Gregory Mullen parent 239c07a9 defa9b61
add runtime types for Template and Page

inlinesplit
src/endpoints/gist.zig added: 193, removed: 37, total 156
@@ -66,7 +66,7 @@ fn post(ctx: *Context) Error!void {
}
 
fn new(ctx: *Context) Error!void {
const tmpl = Template.findTemplate("gist_new.html");
//const tmpl = Template.findTemplate("gist_new.html");
// TODO move this back into context somehow
var btns = [1]Template.Structs.Navbuttons{
.{
@@ -77,7 +77,7 @@ fn new(ctx: *Context) Error!void {
 
var files = [1]Template.Structs.Gistfiles{.{}};
 
var page = GistNewPage.init(tmpl, .{
var page = GistNewPage.init(.{
.meta_head = .{
.open_graph = .{
.title = "Create A New Gist",
@@ -107,8 +107,6 @@ fn toTemplate(a: Allocator, files: []Gist.File) ![]Template.Structs.Gistfiles {
}
 
fn view(ctx: *Context) Error!void {
const tmpl = Template.findTemplate("gist.html");
 
// TODO move this back into context somehow
var btns = [1]Template.Structs.Navbuttons{.{ .name = "inbox", .url = "/inbox" }};
 
@@ -118,7 +116,7 @@ fn view(ctx: *Context) Error!void {
const gist = Gist.open(ctx.alloc, hash[0..64].*) catch return error.Unknown;
const files = toTemplate(ctx.alloc, gist.files) catch return error.Unknown;
const og = try std.fmt.allocPrint(ctx.alloc, "A perfect paste from {}", .{Bleach.Html{ .text = gist.owner }});
var page = GistPage.init(tmpl, .{
var page = GistPage.init(.{
.meta_head = .{
.open_graph = .{
.title = og,
 
src/template.zig added: 193, removed: 37, total 156
@@ -168,6 +168,11 @@ pub const DataMap = struct {
}
};
 
pub const TemplateRuntime = struct {
name: []const u8 = "undefined",
blob: []const u8,
};
 
pub const Template = struct {
// path: []const u8,
name: []const u8 = "undefined",
@@ -181,16 +186,12 @@ pub const Template = struct {
}
}
 
pub fn raze(self: *Template) void {
self.ctx.raze();
pub fn page(self: Template, data: DataMap) PageRuntime(DataMap) {
return PageRuntime(DataMap).init(.{ .name = self.name, .blob = self.blob }, data);
}
 
pub fn page(self: Template, data: DataMap) Page(DataMap) {
return Page(DataMap).init(self, data);
}
 
pub fn pageOf(self: Template, comptime Kind: type, data: Kind) Page(Kind) {
return Page(Kind).init(self, data);
pub fn pageOf(self: Template, comptime Kind: type, data: Kind) PageRuntime(Kind) {
return PageRuntime(Kind).init(.{ .name = self.name, .blob = self.blob }, data);
}
 
pub fn format(_: Template, comptime _: []const u8, _: std.fmt.FormatOptions, _: anytype) !void {
@@ -198,15 +199,14 @@ pub const Template = struct {
}
};
 
pub fn Page(comptime PageDataType: type) type {
pub fn PageRuntime(comptime PageDataType: type) type {
return struct {
pub const Self = @This();
pub const Kind = PageDataType;
pub const Live: bool = PageDataType == DataMap;
template: Template = undefined,
template: TemplateRuntime,
data: PageDataType,
 
pub fn init(t: Template, d: PageDataType) Page(PageDataType) {
pub fn init(t: TemplateRuntime, d: PageDataType) PageRuntime(PageDataType) {
return .{
.template = t,
.data = d,
@@ -238,7 +238,165 @@ pub fn Page(comptime PageDataType: type) type {
return null;
}
 
fn formatLive(
fn formatAny(
self: Self,
comptime fmts: []const u8,
ctx: *DataMap,
drct: Directive,
out: anytype,
) anyerror!void {
switch (drct.kind) {
.noun => |noun| {
const var_name = ctx.get(noun.vari);
if (var_name) |v_blob| {
switch (v_blob) {
.slice => |s_blob| try out.writeAll(s_blob),
.block => |_| unreachable,
.reader => |_| unreachable,
}
} else {
if (DEBUG) std.debug.print("[missing var {s}]\n", .{noun.vari});
switch (noun.otherwise) {
.str => |str| try out.writeAll(str),
// Not really an error, just instruct caller to print original text
.ign => return error.IgnoreDirective,
.del => {},
.template => |subt| {
var subpage = subt.page(self.data);
subpage.format(fmts, .{}, out) catch |err| {
std.debug.print("swallowed subpage format error {}\n", .{err});
unreachable;
};
},
}
}
},
.verb => |verb| verb.do(ctx, out) catch unreachable,
}
}
 
fn formatTyped(
self: Self,
comptime fmts: []const u8,
ctx: PageDataType,
drct: Directive,
out: anytype,
) anyerror!void {
switch (drct.kind) {
.noun => |noun| {
const var_name = typeField(noun.vari, ctx);
if (var_name) |data_blob| {
try out.writeAll(data_blob);
} else {
if (DEBUG) std.debug.print("[missing var {s}]\n", .{noun.vari});
switch (noun.otherwise) {
.str => |str| try out.writeAll(str),
// Not really an error, just instruct caller to print original text
.ign => return error.IgnoreDirective,
.del => {},
.template => |subt| {
inline for (std.meta.fields(PageDataType)) |field|
switch (@typeInfo(field.type)) {
.Optional => |otype| {
if (otype.child == []const u8) continue;
 
var local: [0xff]u8 = undefined;
const realname = local[0..makeFieldName(noun.vari[1 .. noun.vari.len - 5], &local)];
if (std.mem.eql(u8, field.name, realname)) {
if (@field(self.data, field.name)) |subdata| {
var subpage = subt.pageOf(otype.child, subdata);
try subpage.format(fmts, .{}, out);
} else std.debug.print(
"sub template data was null for {s}\n",
.{field.name},
);
}
},
.Struct => {
if (std.mem.eql(u8, field.name, noun.vari)) {
const subdata = @field(self.data, field.name);
var subpage = subt.pageOf(@TypeOf(subdata), subdata);
try subpage.format(fmts, .{}, out);
}
},
else => {}, //@compileLog(field.type),
};
},
}
}
},
.verb => |verb| {
verb.doTyped(PageDataType, ctx, out) catch unreachable;
},
}
}
pub fn format(self: Self, comptime fmts: []const u8, _: std.fmt.FormatOptions, out: anytype) !void {
var ctx = self.data;
var blob = self.template.blob;
while (blob.len > 0) {
if (std.mem.indexOf(u8, blob, "<")) |offset| {
try out.writeAll(blob[0..offset]);
blob = blob[offset..];
if (Directive.init(blob)) |drct| {
const end = drct.end;
if (comptime PageDataType == DataMap) {
self.formatAny(fmts, &ctx, drct, out) catch |err| switch (err) {
error.IgnoreDirective => try out.writeAll(blob[0..end]),
else => return err,
};
} else {
self.formatTyped(fmts, ctx, drct, out) catch |err| switch (err) {
error.IgnoreDirective => try out.writeAll(blob[0..end]),
else => return err,
};
}
blob = blob[end..];
} else {
if (std.mem.indexOfPos(u8, blob, 1, "<")) |next| {
try out.writeAll(blob[0..next]);
blob = blob[next..];
} else {
return try out.writeAll(blob);
}
}
continue;
}
return try out.writeAll(blob);
}
}
};
}
 
pub fn Page(comptime template: Template, comptime PageDataType: type) type {
return struct {
pub const Self = @This();
pub const Kind = PageDataType;
pub const PageTemplate = template;
data: PageDataType,
 
pub fn init(d: PageDataType) Page(template, PageDataType) {
return .{ .data = d };
}
 
pub fn build(self: Self, a: Allocator) ![]u8 {
return std.fmt.allocPrint(a, "{}", .{self});
}
 
fn typeField(name: []const u8, data: PageDataType) ?[]const u8 {
var local: [0xff]u8 = undefined;
const realname = local[0..makeFieldName(name, &local)];
inline for (std.meta.fields(PageDataType)) |field| {
if (std.mem.eql(u8, field.name, realname)) {
switch (field.type) {
[]const u8, ?[]const u8 => return @field(data, field.name),
else => return null,
}
}
}
return null;
}
 
fn formatAny(
self: Self,
comptime fmts: []const u8,
ctx: *DataMap,
@@ -332,25 +490,25 @@ pub fn Page(comptime PageDataType: type) type {
}
 
pub fn format(self: Self, comptime fmts: []const u8, _: std.fmt.FormatOptions, out: anytype) !void {
var ctx = self.data;
var blob = self.template.blob;
const ctx = self.data;
var blob = Self.PageTemplate.blob;
while (blob.len > 0) {
if (std.mem.indexOf(u8, blob, "<")) |offset| {
try out.writeAll(blob[0..offset]);
blob = blob[offset..];
if (Directive.init(blob)) |drct| {
const end = drct.end;
if (comptime Self.Live) {
self.formatLive(fmts, &ctx, drct, out) catch |err| switch (err) {
error.IgnoreDirective => try out.writeAll(blob[0..end]),
else => return err,
};
} else {
self.formatTyped(fmts, ctx, drct, out) catch |err| switch (err) {
error.IgnoreDirective => try out.writeAll(blob[0..end]),
else => return err,
};
}
//if (comptime Self.Live) {
// self.formatAny(fmts, &ctx, drct, out) catch |err| switch (err) {
// error.IgnoreDirective => try out.writeAll(blob[0..end]),
// else => return err,
// };
//} else {
self.formatTyped(fmts, ctx, drct, out) catch |err| switch (err) {
error.IgnoreDirective => try out.writeAll(blob[0..end]),
else => return err,
};
//}
blob = blob[end..];
} else {
if (std.mem.indexOfPos(u8, blob, 1, "<")) |next| {
@@ -484,7 +642,7 @@ pub const Directive = struct {
}
 
pub fn foreachTyped(self: Verb, T: type, data: T, out: anytype) anyerror!void {
var p = Page(T){
var p = PageRuntime(T){
.data = data,
.template = .{
.name = self.vari,
@@ -522,7 +680,7 @@ pub const Directive = struct {
}
 
pub fn withTyped(self: Verb, T: type, block: T, out: anytype) anyerror!void {
var p = Page(T){
var p = PageRuntime(T){
.data = block,
.template = .{
.name = self.vari,
@@ -691,9 +849,9 @@ pub fn findTemplate(comptime name: []const u8) Template {
}
 
pub fn PageData(comptime name: []const u8) type {
//const template = findTemplate(name);
const template = findTemplate(name);
const page_data = comptime findPageType(name);
return Page(page_data);
return Page(template, page_data);
}
 
fn intToWord(in: u8) []const u8 {