Allow auto-routing to markdown files

If a route isn't matched but the URI can match directly to a markdown
file, render it directly. This allows putting arbitrary markdown files
in e.g. `/foo/bar/abc.md`, `/foo/bar/def.md` and rendering them as
`/foo/bar/abc.html`, `/foo/bar/def.html`. Since markdown are static
content only (i.e. no template data etc.) it makes sense for them to be
renderable without needing to create a view for each one.
This commit is contained in:
Bob Farrell 2024-03-27 21:30:38 +00:00
parent c2cc9c50f2
commit 625257bcfc
2 changed files with 57 additions and 33 deletions

View File

@ -157,39 +157,11 @@ fn renderHTML(
};
setResponse(request, rendered, .{});
return;
} else if (try jetzig.markdown.render(request.allocator, matched_route, null)) |markdown_content| {
const rendered = self.renderView(matched_route, request, null) catch |err| {
if (isUnhandledError(err)) return err;
const rendered_error = try self.renderInternalServerError(request, err);
setResponse(request, rendered_error, .{});
return;
};
try addTemplateConstants(rendered.view, matched_route);
if (request.getLayout(matched_route)) |layout_name| {
// TODO: Allow user to configure layouts directory other than src/app/views/layouts/
const prefixed_name = try std.mem.concat(
self.allocator,
u8,
&[_][]const u8{ "layouts_", layout_name },
);
defer self.allocator.free(prefixed_name);
if (zmpl.manifest.find(prefixed_name)) |layout| {
rendered.view.data.content = .{ .data = markdown_content };
request.response.content = try layout.render(rendered.view.data);
} else {
try self.logger.WARN("Unknown layout: {s}", .{layout_name});
request.response.content = markdown_content;
}
}
request.response.status_code = rendered.view.status_code;
request.response.content_type = "text/html";
return;
}
}
if (try self.renderMarkdown(request, route)) return;
request.response.content = "";
request.response.status_code = .not_found;
request.response.content_type = "text/html";
@ -217,6 +189,59 @@ fn renderJSON(
}
}
fn renderMarkdown(self: *Self, request: *jetzig.http.Request, maybe_route: ?*jetzig.views.Route) !bool {
const route = maybe_route orelse {
if (request.method != .GET) return false;
const content = try jetzig.markdown.render(request.allocator, request.path.base_path, null) orelse
return false;
const rendered: RenderedView = .{
.view = jetzig.views.View{ .data = request.response_data, .status_code = .ok },
.content = content,
};
setResponse(request, rendered, .{});
return true;
};
const path = try std.mem.join(
request.allocator,
"/",
&[_][]const u8{ route.uri_path, @tagName(route.action) },
);
const markdown_content = try jetzig.markdown.render(request.allocator, path, null) orelse
return false;
const rendered = self.renderView(route, request, null) catch |err| {
if (isUnhandledError(err)) return err;
const rendered_error = try self.renderInternalServerError(request, err);
setResponse(request, rendered_error, .{});
return true;
};
try addTemplateConstants(rendered.view, route);
if (request.getLayout(route)) |layout_name| {
// TODO: Allow user to configure layouts directory other than src/app/views/layouts/
const prefixed_name = try std.mem.concat(
self.allocator,
u8,
&[_][]const u8{ "layouts_", layout_name },
);
defer self.allocator.free(prefixed_name);
if (zmpl.manifest.find(prefixed_name)) |layout| {
rendered.view.data.content = .{ .data = markdown_content };
request.response.content = try layout.render(rendered.view.data);
} else {
try self.logger.WARN("Unknown layout: {s}", .{layout_name});
request.response.content = markdown_content;
}
}
request.response.status_code = rendered.view.status_code;
request.response.content_type = "text/html";
return true;
}
const RenderedView = struct { view: jetzig.views.View, content: []const u8 };
fn renderView(

View File

@ -5,7 +5,7 @@ const jetzig = @import("../jetzig.zig");
const Zmd = @import("zmd").Zmd;
pub fn render(
allocator: std.mem.Allocator,
route: *const jetzig.views.Route,
path: []const u8,
custom_fragments: ?type,
) !?[]const u8 {
const fragments = custom_fragments orelse jetzig.config.get(type, "markdown_fragments");
@ -15,11 +15,10 @@ pub fn render(
try path_buf.appendSlice(&[_][]const u8{ "src", "app", "views" });
var it = std.mem.splitScalar(u8, route.uri_path, '/');
var it = std.mem.splitScalar(u8, path, '/');
while (it.next()) |segment| {
try path_buf.append(segment);
}
try path_buf.append(@tagName(route.action));
const base_path = try std.fs.path.join(allocator, path_buf.items);
defer allocator.free(base_path);