srctree

Gregory Mullen parent 082c09d9 e76c4a6f
add the ability to inject data

This is just wrong, but it's the first pass of where I actually should belong which is to expose this internall only. Verse should provide the API that endpoints can use to fetch this data themselves before editing the page. The real fix for this will include making page.data const so once a page is 'prepared' it can be modified. My only regret is that I didn't see that before now... fix coming
src/template.zig added: 83, removed: 54, total 29
@@ -12,7 +12,7 @@ pub const Directive = @import("template/directive.zig");
pub const DOM = @import("dom.zig");
pub const HTML = @import("html.zig");
 
const Pages = @import("template/page.zig");
pub const Pages = @import("template/page.zig");
pub const Page = Pages.Page;
pub const PageRuntime = Pages.PageRuntime;
 
 
src/template/directive.zig added: 83, removed: 54, total 29
@@ -1,34 +1,14 @@
const std = @import("std");
const eql = std.mem.eql;
const startsWith = std.mem.startsWith;
const indexOf = std.mem.indexOf;
const indexOfPos = std.mem.indexOfPos;
const indexOfAnyPos = std.mem.indexOfAnyPos;
const indexOfScalar = std.mem.indexOfScalar;
const indexOfScalarPos = std.mem.indexOfScalarPos;
const isUpper = std.ascii.isUpper;
const count = std.mem.count;
const isWhitespace = std.ascii.isWhitespace;
const trim = std.mem.trim;
const trimLeft = std.mem.trimLeft;
const whitespace = std.ascii.whitespace[0..];
 
pub const Directive = @This();
 
const PageRuntime = @import("page.zig").PageRuntime;
 
const Template = @import("../template.zig");
 
const dynamic = &Template.dynamic;
const builtin = Template.builtin;
const makeFieldName = Template.makeFieldName;
 
verb: Verb,
noun: []const u8,
otherwise: Otherwise,
known_type: ?KnownType = null,
tag_block: []const u8,
 
pub const Directive = @This();
const Pages = @import("page.zig");
const PageRuntime = Pages.PageRuntime;
const Template = @import("../template.zig");
 
pub const Otherwise = union(enum) {
required: void,
ignore: void,
@@ -446,7 +426,7 @@ pub fn format(d: Directive, comptime _: []const u8, _: std.fmt.FormatOptions, ou
unreachable;
}
 
pub fn formatTyped(d: Directive, comptime T: type, ctx: T, out: anytype) !void {
pub fn formatTyped(d: Directive, comptime T: type, ctx: T, inj: ?Pages.Injector, out: anytype) !void {
switch (d.verb) {
.variable => {
if (d.known_type) |_| return d.doTyped(T, ctx, out);
@@ -475,7 +455,7 @@ pub fn formatTyped(d: Directive, comptime T: type, ctx: T, out: anytype) !void {
if (std.mem.eql(u8, field.name, realname)) {
if (@field(ctx, field.name)) |subdata| {
var subpage = template.pageOf(otype.child, subdata);
try subpage.format("{}", .{}, out);
try subpage.format2("{}", inj, out);
} else std.debug.print(
"sub template data was null for {s}\n",
.{field.name},
@@ -486,7 +466,7 @@ pub fn formatTyped(d: Directive, comptime T: type, ctx: T, out: anytype) !void {
if (std.mem.eql(u8, field.name, noun)) {
const subdata = @field(ctx, field.name);
var subpage = template.pageOf(@TypeOf(subdata), subdata);
try subpage.format("{}", .{}, out);
try subpage.format2("{}", inj, out);
}
},
else => {}, //@compileLog(field.type),
@@ -507,3 +487,22 @@ pub fn formatTyped(d: Directive, comptime T: type, ctx: T, out: anytype) !void {
else => d.doTyped(T, ctx, out) catch unreachable,
}
}
 
const std = @import("std");
const eql = std.mem.eql;
const startsWith = std.mem.startsWith;
const indexOf = std.mem.indexOf;
const indexOfPos = std.mem.indexOfPos;
const indexOfAnyPos = std.mem.indexOfAnyPos;
const indexOfScalar = std.mem.indexOfScalar;
const indexOfScalarPos = std.mem.indexOfScalarPos;
const isUpper = std.ascii.isUpper;
const count = std.mem.count;
const isWhitespace = std.ascii.isWhitespace;
const trim = std.mem.trim;
const trimLeft = std.mem.trimLeft;
const whitespace = std.ascii.whitespace[0..];
 
const dynamic = &Template.dynamic;
const builtin = Template.builtin;
const makeFieldName = Template.makeFieldName;
 
src/template/page.zig added: 83, removed: 54, total 29
@@ -1,14 +1,12 @@
const std = @import("std");
const is_test = @import("builtin").is_test;
const Allocator = std.mem.Allocator;
const AnyWriter = std.io.AnyWriter;
const eql = std.mem.eql;
const indexOfScalar = std.mem.indexOfScalar;
 
const Templates = @import("../template.zig");
const Template = Templates.Template;
const Directive = Templates.Directive;
 
pub const Injector = struct {
ctx: *anyopaque,
func: *const fn (*anyopaque, []const u8) ?[]const u8,
};
 
pub fn PageRuntime(comptime PageDataType: type) type {
return struct {
pub const Self = @This();
@@ -23,7 +21,11 @@ pub fn PageRuntime(comptime PageDataType: type) type {
};
}
 
pub fn format(self: Self, comptime _: []const u8, _: std.fmt.FormatOptions, out: anytype) !void {
pub fn format(self: Self, comptime f: []const u8, _: std.fmt.FormatOptions, out: anytype) !void {
return try self.format2(f, null, out);
}
 
pub fn format2(self: Self, comptime _: []const u8, injt: ?Injector, out: anytype) !void {
//var ctx = self.data;
var blob = self.template.blob;
while (blob.len > 0) {
@@ -32,11 +34,20 @@ pub fn PageRuntime(comptime PageDataType: type) type {
blob = blob[offset..];
if (Directive.init(blob)) |drct| {
const end = drct.tag_block.len;
drct.formatTyped(PageDataType, self.data, out) catch |err| switch (err) {
drct.formatTyped(PageDataType, self.data, injt, out) catch |err| switch (err) {
error.IgnoreDirective => try out.writeAll(blob[0..end]),
error.VariableMissing => {
if (!is_test) std.debug.print("Template Error, variable missing {{{s}}}\n", .{blob[0..end]});
try out.writeAll(blob[0..end]);
if (injt) |inj| {
if (inj.func(inj.ctx, blob[0..end])) |str| {
try out.writeAll(str);
} else {
if (!is_test) log.err("Template Error, variable missing {{{s}}} Injection failed", .{blob[0..end]});
try out.writeAll(blob[0..end]);
}
} else {
if (!is_test) log.err("Template Error, variable missing {{{s}}}", .{blob[0..end]});
try out.writeAll(blob[0..end]);
}
},
else => return err,
};
@@ -69,7 +80,11 @@ pub fn Page(comptime template: Template, comptime PageDataType: type) type {
return .{ .data = d };
}
 
pub fn format(self: Self, comptime _: []const u8, _: std.fmt.FormatOptions, out: anytype) !void {
pub fn format(self: Self, comptime f: []const u8, _: std.fmt.FormatOptions, out: anytype) !void {
return try self.format2(f, null, out);
}
 
pub fn format2(self: Self, comptime _: []const u8, injt: ?Injector, out: anytype) !void {
var blob = Self.PageTemplate.blob;
while (blob.len > 0) {
if (indexOfScalar(u8, blob, '<')) |offset| {
@@ -78,11 +93,20 @@ pub fn Page(comptime template: Template, comptime PageDataType: type) type {
 
if (Directive.init(blob)) |drct| {
const end = drct.tag_block.len;
drct.formatTyped(PageDataType, self.data, out) catch |err| switch (err) {
drct.formatTyped(PageDataType, self.data, injt, out) catch |err| switch (err) {
error.IgnoreDirective => try out.writeAll(blob[0..end]),
error.VariableMissing => {
if (!is_test) std.debug.print("Template Error, variable missing {{{s}}}\n", .{blob[0..end]});
try out.writeAll(blob[0..end]);
if (injt) |inj| {
if (inj.func(inj.ctx, blob[0..end])) |str| {
try out.writeAll(str);
} else {
if (!is_test) log.err("Template Error, variable missing {{{s}}} Injection failed", .{blob[0..end]});
try out.writeAll(blob[0..end]);
}
} else {
if (!is_test) log.err("Template Error, variable missing {{{s}}}", .{blob[0..end]});
try out.writeAll(blob[0..end]);
}
},
else => return err,
};
@@ -103,3 +127,11 @@ pub fn Page(comptime template: Template, comptime PageDataType: type) type {
}
};
}
 
const std = @import("std");
const is_test = @import("builtin").is_test;
const Allocator = std.mem.Allocator;
const AnyWriter = std.io.AnyWriter;
const eql = std.mem.eql;
const indexOfScalar = std.mem.indexOfScalar;
const log = std.log.scoped(.Verse);
 
src/verse.zig added: 83, removed: 54, total 29
@@ -27,6 +27,7 @@ uri: UriIter,
// TODO fix this unstable API
auth: Auth,
route_ctx: ?*const anyopaque = null,
page_injector: ?*const fn (*anyopaque, []const u8) ?[]const u8 = null,
 
// Raw move from response.zig
headers: Headers,
@@ -226,16 +227,13 @@ pub fn redirect(vrs: *Verse, loc: []const u8, see_other: bool) !void {
/// sendPage will flush headers to the client before sending Page data
pub fn sendPage(vrs: *Verse, page: anytype) NetworkError!void {
try vrs.quickStart();
const loggedin = if (vrs.auth.valid()) "<a href=\"#\">Logged In</a>" else "Public";
const T = @TypeOf(page.*);
if (@hasField(T, "data") and @hasField(@TypeOf(page.data), "body_header")) {
page.data.body_header.?.nav.?.nav_auth = loggedin;
}
 
const inj: ?Template.Pages.Injector = if (vrs.page_injector) |pj| .{ .ctx = vrs, .func = pj } else null;
 
switch (vrs.downstream) {
.http, .zwsgi => |stream| {
const w = stream.writer();
page.format("{}", .{}, w) catch |err| switch (err) {
page.format2("{}", inj, w) catch |err| switch (err) {
else => log.err("Page Build Error {}", .{err}),
};
},