mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 22:16:08 +00:00
Closes #48: Simplify route generation
Generate an array of `jetzig.views.Route` in `GenerateRoutes.zig` exe instead of a meta-route that we later translate into an actual route. This makes things much simpler for static routes at build time and dynamic routes at run time as we no longer need to use comptime - we just have an array of routes ready-made.
This commit is contained in:
parent
800b72eeb9
commit
116668be3e
@ -2,7 +2,7 @@ const std = @import("std");
|
||||
|
||||
pub const jetzig = @import("jetzig");
|
||||
|
||||
pub const routes = @import("routes").routes;
|
||||
pub const routes = @import("routes");
|
||||
|
||||
// Override default settings in `jetzig.config` here:
|
||||
pub const jetzig_options = struct {
|
||||
@ -92,5 +92,5 @@ pub fn main() !void {
|
||||
const app = try jetzig.init(allocator);
|
||||
defer app.deinit();
|
||||
|
||||
try app.start(comptime jetzig.route(routes));
|
||||
try app.start(routes, .{});
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ const Function = struct {
|
||||
path: []const u8,
|
||||
source: []const u8,
|
||||
params: std.ArrayList([]const u8),
|
||||
static: bool = false,
|
||||
|
||||
/// The full name of a route. This **must** match the naming convention used by static route
|
||||
/// compilation.
|
||||
@ -128,8 +129,9 @@ pub fn generateRoutes(self: *Self) !void {
|
||||
std.sort.pdq(Function, self.dynamic_routes.items, {}, Function.lessThanFn);
|
||||
|
||||
try writer.writeAll(
|
||||
\\pub const routes = struct {
|
||||
\\ pub const static = .{
|
||||
\\const jetzig = @import("jetzig");
|
||||
\\
|
||||
\\pub const routes = [_]jetzig.views.Route{
|
||||
\\
|
||||
);
|
||||
|
||||
@ -137,21 +139,14 @@ pub fn generateRoutes(self: *Self) !void {
|
||||
try self.writeRoute(writer, static_route);
|
||||
}
|
||||
|
||||
try writer.writeAll(
|
||||
\\ };
|
||||
\\
|
||||
\\ pub const dynamic = .{
|
||||
\\
|
||||
);
|
||||
|
||||
for (self.dynamic_routes.items) |dynamic_route| {
|
||||
try self.writeRoute(writer, dynamic_route);
|
||||
const name = try dynamic_route.fullName(self.allocator);
|
||||
defer self.allocator.free(name);
|
||||
std.debug.print("[jetzig] Imported route: {s}\n", .{name});
|
||||
}
|
||||
|
||||
try writer.writeAll(" };\n");
|
||||
std.debug.print("[jetzig] Imported {} route(s)\n", .{self.dynamic_routes.items.len});
|
||||
|
||||
try writer.writeAll("};");
|
||||
|
||||
// std.debug.print("routes.zig\n{s}\n", .{self.buffer.items});
|
||||
@ -166,33 +161,19 @@ fn writeRoute(self: *Self, writer: std.ArrayList(u8).Writer, route: Function) !v
|
||||
|
||||
const output_template =
|
||||
\\ .{{
|
||||
\\ .name = "{s}",
|
||||
\\ .action = "{s}",
|
||||
\\ .view_name = "{s}",
|
||||
\\ .uri_path = "{s}",
|
||||
\\ .template = "{s}",
|
||||
\\ .module = @import("{s}"),
|
||||
\\ .function = @import("{s}").{s},
|
||||
\\ .params = {s},
|
||||
\\ .name = "{0s}",
|
||||
\\ .action = .{1s},
|
||||
\\ .view_name = "{2s}",
|
||||
\\ .view = jetzig.views.Route.ViewType{{ .{3s} = .{{ .{1s} = @import("{7s}").{1s} }} }},
|
||||
\\ .static = {4s},
|
||||
\\ .uri_path = "{5s}",
|
||||
\\ .template = "{6s}",
|
||||
\\ .layout = if (@hasDecl(@import("{7s}"), "layout")) @import("{7s}").layout else null,
|
||||
\\ .json_params = &[_][]const u8 {{ {8s} }},
|
||||
\\ }},
|
||||
\\
|
||||
;
|
||||
|
||||
var params_buf = std.ArrayList(u8).init(self.allocator);
|
||||
const params_writer = params_buf.writer();
|
||||
defer params_buf.deinit();
|
||||
if (route.params.items.len > 0) {
|
||||
try params_writer.writeAll(".{\n");
|
||||
for (route.params.items) |item| {
|
||||
try params_writer.writeAll(" ");
|
||||
try params_writer.writeAll(item);
|
||||
try params_writer.writeAll(",\n");
|
||||
}
|
||||
try params_writer.writeAll(" }");
|
||||
} else {
|
||||
try params_writer.writeAll(".{}");
|
||||
}
|
||||
|
||||
const module_path = try self.allocator.dupe(u8, route.path);
|
||||
defer self.allocator.free(module_path);
|
||||
|
||||
@ -205,12 +186,12 @@ fn writeRoute(self: *Self, writer: std.ArrayList(u8).Writer, route: Function) !v
|
||||
full_name,
|
||||
route.name,
|
||||
route.view_name,
|
||||
if (route.static) "static" else "dynamic",
|
||||
if (route.static) "true" else "false",
|
||||
uri_path,
|
||||
full_name,
|
||||
module_path,
|
||||
module_path,
|
||||
route.name,
|
||||
params_buf.items,
|
||||
try std.mem.join(self.allocator, ", \n", route.params.items),
|
||||
});
|
||||
|
||||
defer self.allocator.free(output);
|
||||
@ -237,13 +218,14 @@ fn generateRoutesForView(self: *Self, views_dir: std.fs.Dir, path: []const u8) !
|
||||
switch (tag) {
|
||||
.fn_proto_multi => {
|
||||
const function = try self.parseFunction(index, path, source);
|
||||
if (function) |capture| {
|
||||
if (function) |*capture| {
|
||||
for (capture.args) |arg| {
|
||||
if (std.mem.eql(u8, try arg.typeBasename(), "StaticRequest")) {
|
||||
try static_routes.append(capture);
|
||||
@constCast(capture).static = true;
|
||||
try static_routes.append(capture.*);
|
||||
}
|
||||
if (std.mem.eql(u8, try arg.typeBasename(), "Request")) {
|
||||
try dynamic_routes.append(capture);
|
||||
try dynamic_routes.append(capture.*);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,42 +19,16 @@ pub fn main() !void {
|
||||
fn compileStaticRoutes(allocator: std.mem.Allocator) !void {
|
||||
std.fs.cwd().deleteTree("static") catch {};
|
||||
|
||||
inline for (routes.static) |static_route| {
|
||||
const static_view = jetzig.views.Route.ViewType{
|
||||
.static = @unionInit(
|
||||
jetzig.views.Route.StaticViewType,
|
||||
static_route.action,
|
||||
static_route.function,
|
||||
),
|
||||
};
|
||||
var count: usize = 0;
|
||||
|
||||
comptime var static_params_len = 0;
|
||||
inline for (static_route.params) |_| static_params_len += 1;
|
||||
comptime var params: [static_params_len][]const u8 = undefined;
|
||||
inline for (static_route.params, 0..) |json, index| params[index] = json;
|
||||
for (routes) |route| {
|
||||
if (!route.static) continue;
|
||||
|
||||
const layout: ?[]const u8 = if (@hasDecl(static_route.module, "layout"))
|
||||
static_route.module.layout
|
||||
else
|
||||
null;
|
||||
|
||||
const route = jetzig.views.Route{
|
||||
.name = static_route.name,
|
||||
.action = @field(jetzig.views.Route.Action, static_route.action),
|
||||
.view_name = static_route.view_name,
|
||||
.view = static_view,
|
||||
.static = true,
|
||||
.uri_path = static_route.uri_path,
|
||||
.layout = layout,
|
||||
.template = static_route.template,
|
||||
.json_params = ¶ms,
|
||||
};
|
||||
|
||||
if (static_params_len > 0) {
|
||||
inline for (static_route.params, 0..) |json, index| {
|
||||
if (route.json_params.len > 0) {
|
||||
for (route.json_params, 0..) |json, index| {
|
||||
var request = try jetzig.http.StaticRequest.init(allocator, json);
|
||||
defer request.deinit();
|
||||
try writeContent(allocator, route, &request, index);
|
||||
try writeContent(allocator, route, &request, index, &count);
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,18 +38,20 @@ fn compileStaticRoutes(allocator: std.mem.Allocator) !void {
|
||||
.index, .post => {
|
||||
var request = try jetzig.http.StaticRequest.init(allocator, "{}");
|
||||
defer request.deinit();
|
||||
try writeContent(allocator, route, &request, null);
|
||||
try writeContent(allocator, route, &request, null, &count);
|
||||
},
|
||||
inline else => {},
|
||||
}
|
||||
}
|
||||
std.debug.print("[jetzig] Compiled {} static output(s)\n", .{count});
|
||||
}
|
||||
|
||||
fn writeContent(
|
||||
allocator: std.mem.Allocator,
|
||||
comptime route: jetzig.views.Route,
|
||||
route: jetzig.views.Route,
|
||||
request: *jetzig.http.StaticRequest,
|
||||
index: ?usize,
|
||||
count: *usize,
|
||||
) !void {
|
||||
const index_suffix = if (index) |capture|
|
||||
try std.fmt.allocPrint(allocator, "_{}", .{capture})
|
||||
@ -100,7 +76,7 @@ fn writeContent(
|
||||
try json_file.writeAll(try view.data.toJson());
|
||||
defer json_file.close();
|
||||
|
||||
std.debug.print("[jetzig] Compiled static route: {s}\n", .{json_path});
|
||||
count.* += 1;
|
||||
|
||||
const html_content = try renderZmplTemplate(allocator, route, view) orelse
|
||||
try renderMarkdown(allocator, route, view) orelse
|
||||
@ -116,7 +92,7 @@ fn writeContent(
|
||||
try html_file.writeAll(content);
|
||||
defer html_file.close();
|
||||
allocator.free(content);
|
||||
std.debug.print("[jetzig] Compiled static route: {s}\n", .{html_path});
|
||||
count.* += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,84 +96,3 @@ pub fn init(allocator: std.mem.Allocator) !App {
|
||||
.allocator = allocator,
|
||||
};
|
||||
}
|
||||
|
||||
// Receives an array of imported modules and detects functions defined on them.
|
||||
// Each detected function is stored as a Route which can be accessed at runtime to route requests
|
||||
// to the appropriate View.
|
||||
pub fn route(comptime routes: anytype) []views.Route {
|
||||
var size: usize = 0;
|
||||
|
||||
for (routes.dynamic) |_| {
|
||||
size += 1;
|
||||
}
|
||||
|
||||
for (routes.static) |_| {
|
||||
size += 1;
|
||||
}
|
||||
|
||||
var detected: [size]views.Route = undefined;
|
||||
var index: usize = 0;
|
||||
|
||||
for (routes.dynamic) |dynamic_route| {
|
||||
const view = views.Route.ViewType{
|
||||
.dynamic = @unionInit(
|
||||
views.Route.DynamicViewType,
|
||||
dynamic_route.action,
|
||||
dynamic_route.function,
|
||||
),
|
||||
};
|
||||
|
||||
const layout: ?[]const u8 = if (@hasDecl(dynamic_route.module, "layout"))
|
||||
dynamic_route.module.layout
|
||||
else
|
||||
null;
|
||||
|
||||
detected[index] = .{
|
||||
.name = dynamic_route.name,
|
||||
.action = @field(views.Route.Action, dynamic_route.action),
|
||||
.view_name = dynamic_route.view_name,
|
||||
.view = view,
|
||||
.static = false,
|
||||
.uri_path = dynamic_route.uri_path,
|
||||
.layout = layout,
|
||||
.template = dynamic_route.template,
|
||||
.json_params = &.{},
|
||||
};
|
||||
index += 1;
|
||||
}
|
||||
|
||||
for (routes.static) |static_route| {
|
||||
const view = views.Route.ViewType{
|
||||
.static = @unionInit(
|
||||
views.Route.StaticViewType,
|
||||
static_route.action,
|
||||
static_route.function,
|
||||
),
|
||||
};
|
||||
|
||||
comptime var params_size = 0;
|
||||
inline for (static_route.params) |_| params_size += 1;
|
||||
comptime var static_params: [params_size][]const u8 = undefined;
|
||||
inline for (static_route.params, 0..) |json, params_index| static_params[params_index] = json;
|
||||
|
||||
const layout: ?[]const u8 = if (@hasDecl(static_route.module, "layout"))
|
||||
static_route.module.layout
|
||||
else
|
||||
null;
|
||||
|
||||
detected[index] = .{
|
||||
.name = static_route.name,
|
||||
.action = @field(views.Route.Action, static_route.action),
|
||||
.view_name = static_route.view_name,
|
||||
.view = view,
|
||||
.static = true,
|
||||
.uri_path = static_route.uri_path,
|
||||
.layout = layout,
|
||||
.template = static_route.template,
|
||||
.json_params = &static_params,
|
||||
};
|
||||
index += 1;
|
||||
}
|
||||
|
||||
return &detected;
|
||||
}
|
||||
|
@ -14,35 +14,43 @@ pub fn deinit(self: Self) void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
// Not used yet, but allows us to add new options to `start()` without breaking
|
||||
// backward-compatibility.
|
||||
const AppOptions = struct {};
|
||||
|
||||
/// Starts an application. `routes` should be `@import("routes").routes`, a generated file
|
||||
/// automatically created at build time. `templates` should be
|
||||
/// `@import("src/app/views/zmpl.manifest.zig").templates`, created by Zmpl at compile time.
|
||||
pub fn start(self: Self, comptime_routes: []jetzig.views.Route) !void {
|
||||
pub fn start(self: Self, routes_module: type, options: AppOptions) !void {
|
||||
_ = options; // See `AppOptions`
|
||||
|
||||
var mime_map = jetzig.http.mime.MimeMap.init(self.allocator);
|
||||
defer mime_map.deinit();
|
||||
try mime_map.build();
|
||||
|
||||
var routes = std.ArrayList(*jetzig.views.Route).init(self.allocator);
|
||||
|
||||
for (comptime_routes) |*comptime_route| {
|
||||
for (routes_module.routes) |const_route| {
|
||||
var route = try self.allocator.create(jetzig.views.Route);
|
||||
route.* = jetzig.views.Route{
|
||||
.name = comptime_route.name,
|
||||
.action = comptime_route.action,
|
||||
.view_name = comptime_route.view_name,
|
||||
.uri_path = comptime_route.uri_path,
|
||||
.view = comptime_route.view,
|
||||
.static_view = comptime_route.static_view,
|
||||
.static = comptime_route.static,
|
||||
.render = comptime_route.render,
|
||||
.renderStatic = comptime_route.renderStatic,
|
||||
.layout = comptime_route.layout,
|
||||
.template = comptime_route.template,
|
||||
.json_params = comptime_route.json_params,
|
||||
route.* = .{
|
||||
.name = const_route.name,
|
||||
.action = const_route.action,
|
||||
.view_name = const_route.view_name,
|
||||
.uri_path = const_route.uri_path,
|
||||
.view = const_route.view,
|
||||
.static_view = const_route.static_view,
|
||||
.static = const_route.static,
|
||||
.render = const_route.render,
|
||||
.renderStatic = const_route.renderStatic,
|
||||
.layout = const_route.layout,
|
||||
.template = const_route.template,
|
||||
.json_params = const_route.json_params,
|
||||
};
|
||||
|
||||
try route.initParams(self.allocator);
|
||||
try routes.append(route);
|
||||
}
|
||||
|
||||
defer routes.deinit();
|
||||
defer for (routes.items) |route| {
|
||||
route.deinitParams();
|
||||
|
@ -47,7 +47,7 @@ render: RenderFn = renderFn,
|
||||
renderStatic: RenderStaticFn = renderStaticFn,
|
||||
layout: ?[]const u8,
|
||||
template: []const u8,
|
||||
json_params: [][]const u8,
|
||||
json_params: []const []const u8,
|
||||
params: std.ArrayList(*jetzig.data.Data) = undefined,
|
||||
|
||||
/// Initializes a route's static params on server launch. Converts static params (JSON strings)
|
||||
|
Loading…
x
Reference in New Issue
Block a user