Merge pull request #49 from jetzig-framework/simplify-route-generation

Simplify route generation
This commit is contained in:
bobf 2024-04-02 21:11:00 +01:00 committed by GitHub
commit b94845a415
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 60 additions and 175 deletions

View File

@ -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, .{});
}

View File

@ -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.*);
}
}
}

View File

@ -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 = &params,
};
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;
}
}

View File

@ -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;
}

View File

@ -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();

View File

@ -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)