mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 14:06:08 +00:00
Merge pull request #44 from jetzig-framework/nested-routes
Implement nested routes
This commit is contained in:
commit
d368850b1c
22
demo/src/app/views/nested/route/example.zig
Normal file
22
demo/src/app/views/nested/route/example.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const std = @import("std");
|
||||
const jetzig = @import("jetzig");
|
||||
|
||||
pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
||||
_ = data;
|
||||
return request.render(.ok);
|
||||
}
|
||||
|
||||
pub const static_params = .{
|
||||
.get = .{
|
||||
.{ .id = "foo", .params = .{ .foo = "bar" } },
|
||||
.{ .id = "foo" },
|
||||
},
|
||||
};
|
||||
|
||||
pub fn get(id: []const u8, request: *jetzig.StaticRequest, data: *jetzig.Data) !jetzig.View {
|
||||
var object = try data.object();
|
||||
try object.put("id", data.string(id));
|
||||
const params = try request.params();
|
||||
if (params.get("foo")) |value| try object.put("foo", value);
|
||||
return request.render(.ok);
|
||||
}
|
3
demo/src/app/views/nested/route/example/index.zmpl
Normal file
3
demo/src/app/views/nested/route/example/index.zmpl
Normal file
@ -0,0 +1,3 @@
|
||||
<div>
|
||||
<span>Content goes here</span>
|
||||
</div>
|
@ -1,10 +0,0 @@
|
||||
// Zmpl template manifest.
|
||||
// This file is automatically generated at build time. Manual edits will be discarded.
|
||||
// This file should _not_ be stored in version control.
|
||||
pub const templates = struct {
|
||||
pub const static_index = @import("static/.index.zmpl.compiled.zig");
|
||||
pub const static_get = @import("static/.get.zmpl.compiled.zig");
|
||||
pub const root_index = @import("root/.index.zmpl.compiled.zig");
|
||||
pub const quotes_post = @import("quotes/.post.zmpl.compiled.zig");
|
||||
pub const quotes_get = @import("quotes/.get.zmpl.compiled.zig");
|
||||
};
|
@ -23,23 +23,27 @@ const Function = struct {
|
||||
/// compilation.
|
||||
/// path: `src/app/views/iguanas.zig`, action: `index` => `iguanas_index`
|
||||
pub fn fullName(self: @This(), allocator: std.mem.Allocator) ![]const u8 {
|
||||
// XXX: Currently we do not support nested routes, so we will need to adjust this if we
|
||||
// add nested routes in future.
|
||||
const extension = std.fs.path.extension(self.path);
|
||||
const basename = std.fs.path.basename(self.path);
|
||||
const name = basename[0 .. basename.len - extension.len];
|
||||
const relative_path = try std.fs.path.relative(allocator, "src/app/views/", self.path);
|
||||
defer allocator.free(relative_path);
|
||||
|
||||
return std.mem.concat(allocator, u8, &[_][]const u8{ name, "_", self.name });
|
||||
const path = relative_path[0 .. relative_path.len - std.fs.path.extension(relative_path).len];
|
||||
std.mem.replaceScalar(u8, path, '\\', '/');
|
||||
std.mem.replaceScalar(u8, path, '/', '_');
|
||||
|
||||
return std.mem.concat(allocator, u8, &[_][]const u8{ path, "_", self.name });
|
||||
}
|
||||
|
||||
/// The path used to match the route. Resource ID and extension is not included here and is
|
||||
/// appended as needed during matching logic at run time.
|
||||
pub fn uriPath(self: @This(), allocator: std.mem.Allocator) ![]const u8 {
|
||||
const basename = std.fs.path.basename(self.path);
|
||||
const name = basename[0 .. basename.len - std.fs.path.extension(basename).len];
|
||||
if (std.mem.eql(u8, name, "root")) return try allocator.dupe(u8, "/");
|
||||
const relative_path = try std.fs.path.relative(allocator, "src/app/views/", self.path);
|
||||
defer allocator.free(relative_path);
|
||||
|
||||
return try std.mem.concat(allocator, u8, &[_][]const u8{ "/", name });
|
||||
const path = relative_path[0 .. relative_path.len - std.fs.path.extension(relative_path).len];
|
||||
std.mem.replaceScalar(u8, path, '\\', '/');
|
||||
if (std.mem.eql(u8, path, "root")) return try allocator.dupe(u8, "/");
|
||||
|
||||
return try std.mem.concat(allocator, u8, &[_][]const u8{ "/", path });
|
||||
}
|
||||
|
||||
pub fn lessThanFn(context: void, lhs: @This(), rhs: @This()) bool {
|
||||
@ -247,6 +251,7 @@ fn generateRoutesForView(self: *Self, views_dir: std.fs.Dir, path: []const u8) !
|
||||
.simple_var_decl => {
|
||||
const decl = self.ast.simpleVarDecl(asNodeIndex(index));
|
||||
if (self.isStaticParamsDecl(decl)) {
|
||||
self.data.reset();
|
||||
const params = try self.data.object();
|
||||
try self.parseStaticParamsDecl(decl, params);
|
||||
static_params = self.data.value;
|
||||
|
@ -62,6 +62,7 @@ pub fn init(allocator: std.mem.Allocator) Environment {
|
||||
/// Generate server initialization options using command line args with defaults.
|
||||
pub fn getServerOptions(self: Environment) !jetzig.http.Server.ServerOptions {
|
||||
const options = try args.parseForCurrentProcess(Options, self.allocator, .print);
|
||||
defer options.deinit();
|
||||
|
||||
if (options.options.help) {
|
||||
const writer = std.io.getStdErr().writer();
|
||||
|
@ -473,32 +473,31 @@ fn staticPath(request: *jetzig.http.Request, route: jetzig.views.Route) !?[]cons
|
||||
};
|
||||
|
||||
for (route.params.items, 0..) |static_params, index| {
|
||||
if (try static_params.getValue("params")) |expected_params| {
|
||||
switch (route.action) {
|
||||
.index, .post => {},
|
||||
inline else => {
|
||||
if (try static_params.getValue("id")) |id| {
|
||||
switch (id.*) {
|
||||
.string => |capture| {
|
||||
if (!std.mem.eql(u8, capture.value, request.path.resource_id)) continue;
|
||||
},
|
||||
// Should be unreachable - this means generated `routes.zig` is incoherent:
|
||||
inline else => return error.JetzigRouteError,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
if (!expected_params.eql(params)) continue;
|
||||
|
||||
const index_fmt = try std.fmt.allocPrint(request.allocator, "{}", .{index});
|
||||
defer request.allocator.free(index_fmt);
|
||||
|
||||
return try std.mem.concat(
|
||||
request.allocator,
|
||||
u8,
|
||||
&[_][]const u8{ route.name, "_", index_fmt, extension },
|
||||
);
|
||||
const expected_params = try static_params.getValue("params");
|
||||
switch (route.action) {
|
||||
.index, .post => {},
|
||||
inline else => {
|
||||
const id = try static_params.getValue("id");
|
||||
if (id == null) return error.JetzigRouteError; // `routes.zig` is incoherent.
|
||||
switch (id.?.*) {
|
||||
.string => |capture| {
|
||||
if (!std.mem.eql(u8, capture.value, request.path.resource_id)) continue;
|
||||
},
|
||||
// `routes.zig` is incoherent.
|
||||
inline else => return error.JetzigRouteError,
|
||||
}
|
||||
},
|
||||
}
|
||||
if (expected_params != null and !expected_params.?.eql(params)) continue;
|
||||
|
||||
const index_fmt = try std.fmt.allocPrint(request.allocator, "{}", .{index});
|
||||
defer request.allocator.free(index_fmt);
|
||||
|
||||
return try std.mem.concat(
|
||||
request.allocator,
|
||||
u8,
|
||||
&[_][]const u8{ route.name, "_", index_fmt, extension },
|
||||
);
|
||||
}
|
||||
|
||||
switch (route.action) {
|
||||
|
@ -27,7 +27,12 @@ pub fn render(
|
||||
const full_path = try std.mem.concat(allocator, u8, &[_][]const u8{ base_path, ".md" });
|
||||
defer allocator.free(full_path);
|
||||
|
||||
const stat = try std.fs.cwd().statFile(full_path);
|
||||
const stat = std.fs.cwd().statFile(full_path) catch |err| {
|
||||
return switch (err) {
|
||||
error.FileNotFound => null,
|
||||
else => err,
|
||||
};
|
||||
};
|
||||
const markdown_content = std.fs.cwd().readFileAlloc(allocator, full_path, stat.size) catch |err| {
|
||||
switch (err) {
|
||||
error.FileNotFound => return null,
|
||||
|
Loading…
x
Reference in New Issue
Block a user