mirror of
https://github.com/zigzap/zap.git
synced 2025-10-20 15:14:08 +00:00
Provide defaults to unprovided restful method handler
Make `get`, `post`, ... methods optional. Check whether these method exist at comptime. When no corresponding method is provided, the handler simply return immediately. Since it uses comptime, hopefully it should not add any checks at runtime.
This commit is contained in:
parent
ec7cac6f6a
commit
c0cc025eda
4 changed files with 46 additions and 61 deletions
|
@ -58,14 +58,6 @@ const MyEndpoint = struct {
|
||||||
);
|
);
|
||||||
try r.sendBody(response);
|
try r.sendBody(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// not implemented, don't care
|
|
||||||
pub fn post(_: *MyEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn put(_: *MyEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn delete(_: *MyEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn patch(_: *MyEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn options(_: *MyEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn head(_: *MyEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
@ -83,8 +75,8 @@ pub fn main() !void {
|
||||||
// App is the type
|
// App is the type
|
||||||
// app is the instance
|
// app is the instance
|
||||||
const App = zap.App.Create(MyContext);
|
const App = zap.App.Create(MyContext);
|
||||||
var app = try App.init(allocator, &my_context, .{});
|
try App.init(allocator, &my_context, .{});
|
||||||
defer app.deinit();
|
defer App.deinit();
|
||||||
|
|
||||||
// create mini endpoint
|
// create mini endpoint
|
||||||
var ep: MyEndpoint = .{
|
var ep: MyEndpoint = .{
|
||||||
|
@ -101,10 +93,10 @@ pub fn main() !void {
|
||||||
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
|
||||||
|
|
||||||
// make the authenticating endpoint known to the app
|
// make the authenticating endpoint known to the app
|
||||||
try app.register(&auth_ep);
|
try App.register(&auth_ep);
|
||||||
|
|
||||||
// listen
|
// listen
|
||||||
try app.listen(.{
|
try App.listen(.{
|
||||||
.interface = "0.0.0.0",
|
.interface = "0.0.0.0",
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
});
|
});
|
||||||
|
|
|
@ -59,15 +59,7 @@ const SimpleEndpoint = struct {
|
||||||
try r.sendBody(response_text);
|
try r.sendBody(response_text);
|
||||||
std.time.sleep(std.time.ns_per_ms * 300);
|
std.time.sleep(std.time.ns_per_ms * 300);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
// empty stubs for all other request methods
|
|
||||||
pub fn post(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn put(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn delete(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn patch(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn options(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn head(_: *SimpleEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
};
|
|
||||||
|
|
||||||
const StopEndpoint = struct {
|
const StopEndpoint = struct {
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
|
@ -82,12 +74,6 @@ const StopEndpoint = struct {
|
||||||
, .{context.*.db_connection});
|
, .{context.*.db_connection});
|
||||||
zap.stop();
|
zap.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn put(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn delete(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn patch(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
pub fn options(_: *StopEndpoint, _: Allocator, _: *MyContext, _: zap.Request) !void {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
@ -104,19 +90,19 @@ pub fn main() !void {
|
||||||
|
|
||||||
// create an App instance
|
// create an App instance
|
||||||
const App = zap.App.Create(MyContext);
|
const App = zap.App.Create(MyContext);
|
||||||
var app = try App.init(allocator, &my_context, .{});
|
try App.init(allocator, &my_context, .{});
|
||||||
defer app.deinit();
|
defer App.deinit();
|
||||||
|
|
||||||
// create the endpoints
|
// create the endpoints
|
||||||
var my_endpoint = SimpleEndpoint.init("/test", "some endpoint specific data");
|
var my_endpoint = SimpleEndpoint.init("/test", "some endpoint specific data");
|
||||||
var stop_endpoint: StopEndpoint = .{ .path = "/stop" };
|
var stop_endpoint: StopEndpoint = .{ .path = "/stop" };
|
||||||
//
|
//
|
||||||
// register the endpoints with the app
|
// register the endpoints with the App
|
||||||
try app.register(&my_endpoint);
|
try App.register(&my_endpoint);
|
||||||
try app.register(&stop_endpoint);
|
try App.register(&stop_endpoint);
|
||||||
|
|
||||||
// listen on the network
|
// listen on the network
|
||||||
try app.listen(.{
|
try App.listen(.{
|
||||||
.interface = "0.0.0.0",
|
.interface = "0.0.0.0",
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
});
|
});
|
||||||
|
|
|
@ -86,24 +86,24 @@ pub fn main() !void {
|
||||||
defer std.debug.print("\n\nLeaks detected: {}\n\n", .{gpa.deinit() != .ok});
|
defer std.debug.print("\n\nLeaks detected: {}\n\n", .{gpa.deinit() != .ok});
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
// create an app context
|
// create an App context
|
||||||
var my_context: MyContext = .{ .db_connection = "db connection established!" };
|
var my_context: MyContext = .{ .db_connection = "db connection established!" };
|
||||||
|
|
||||||
// create an App instance
|
// create an App instance
|
||||||
const App = zap.App.Create(MyContext);
|
const App = zap.App.Create(MyContext);
|
||||||
var app = try App.init(allocator, &my_context, .{});
|
try App.init(allocator, &my_context, .{});
|
||||||
defer app.deinit();
|
defer App.deinit();
|
||||||
|
|
||||||
// create the endpoints
|
// create the endpoints
|
||||||
var my_endpoint = ErrorEndpoint.init("/error", "some endpoint specific data");
|
var my_endpoint = ErrorEndpoint.init("/error", "some endpoint specific data");
|
||||||
var stop_endpoint: StopEndpoint = .{ .path = "/stop" };
|
var stop_endpoint: StopEndpoint = .{ .path = "/stop" };
|
||||||
//
|
//
|
||||||
// register the endpoints with the app
|
// register the endpoints with the App
|
||||||
try app.register(&my_endpoint);
|
try App.register(&my_endpoint);
|
||||||
try app.register(&stop_endpoint);
|
try App.register(&stop_endpoint);
|
||||||
|
|
||||||
// listen on the network
|
// listen on the network
|
||||||
try app.listen(.{
|
try App.listen(.{
|
||||||
.interface = "0.0.0.0",
|
.interface = "0.0.0.0",
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
});
|
});
|
||||||
|
|
49
src/App.zig
49
src/App.zig
|
@ -110,14 +110,15 @@ pub fn Create(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn onRequest(self: *Bound, arena: Allocator, app_context: *Context, r: Request) !void {
|
pub fn onRequest(self: *Bound, arena: Allocator, app_context: *Context, r: Request) !void {
|
||||||
|
// TODO: simplitfy this with @tagName?
|
||||||
const ret = switch (r.methodAsEnum()) {
|
const ret = switch (r.methodAsEnum()) {
|
||||||
.GET => self.endpoint.*.get(arena, app_context, r),
|
.GET => callHandlerIfExist("get", self.endpoint, arena, app_context, r),
|
||||||
.POST => self.endpoint.*.post(arena, app_context, r),
|
.POST => callHandlerIfExist("post", self.endpoint, arena, app_context, r),
|
||||||
.PUT => self.endpoint.*.put(arena, app_context, r),
|
.PUT => callHandlerIfExist("put", self.endpoint, arena, app_context, r),
|
||||||
.DELETE => self.endpoint.*.delete(arena, app_context, r),
|
.DELETE => callHandlerIfExist("delete", self.endpoint, arena, app_context, r),
|
||||||
.PATCH => self.endpoint.*.patch(arena, app_context, r),
|
.PATCH => callHandlerIfExist("patch", self.endpoint, arena, app_context, r),
|
||||||
.OPTIONS => self.endpoint.*.options(arena, app_context, r),
|
.OPTIONS => callHandlerIfExist("options", self.endpoint, arena, app_context, r),
|
||||||
.HEAD => self.endpoint.*.head(arena, app_context, r),
|
.HEAD => callHandlerIfExist("head", self.endpoint, arena, app_context, r),
|
||||||
else => error.UnsupportedHtmlRequestMethod,
|
else => error.UnsupportedHtmlRequestMethod,
|
||||||
};
|
};
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -227,8 +228,6 @@ pub fn Create(
|
||||||
if (ret_info.error_union.payload != void) {
|
if (ret_info.error_union.payload != void) {
|
||||||
@compileError("Expected return type of method `" ++ @typeName(T) ++ "." ++ method ++ "` to be !void, got: !" ++ @typeName(ret_info.error_union.payload));
|
@compileError("Expected return type of method `" ++ @typeName(T) ++ "." ++ method ++ "` to be !void, got: !" ++ @typeName(ret_info.error_union.payload));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
@compileError(@typeName(T) ++ " has no method named `" ++ method ++ "`");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,7 +257,7 @@ pub fn Create(
|
||||||
pub fn get(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: Request) anyerror!void {
|
pub fn get(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&request)) {
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
||||||
.AuthOK => self.ep.*.get(arena, context, request),
|
.AuthOK => callHandlerIfExist("get", self.ep, arena, context, request),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -267,7 +266,7 @@ pub fn Create(
|
||||||
pub fn post(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: Request) anyerror!void {
|
pub fn post(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&request)) {
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
||||||
.AuthOK => self.ep.*.post(arena, context, request),
|
.AuthOK => callHandlerIfExist("post", self.ep, arena, context, request),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -276,7 +275,7 @@ pub fn Create(
|
||||||
pub fn put(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
pub fn put(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&request)) {
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
||||||
.AuthOK => self.ep.*.put(arena, context, request),
|
.AuthOK => callHandlerIfExist("put", self.ep, arena, context, request),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -285,7 +284,7 @@ pub fn Create(
|
||||||
pub fn delete(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
pub fn delete(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&request)) {
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
||||||
.AuthOK => self.ep.*.delete(arena, context, request),
|
.AuthOK => callHandlerIfExist("delete", self.ep, arena, context, request),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -294,7 +293,7 @@ pub fn Create(
|
||||||
pub fn patch(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
pub fn patch(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&request)) {
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
||||||
.AuthOK => self.ep.*.patch(arena, context, request),
|
.AuthOK => callHandlerIfExist("patch", self.ep, arena, context, request),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -303,7 +302,7 @@ pub fn Create(
|
||||||
pub fn options(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
pub fn options(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&request)) {
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
||||||
.AuthOK => self.ep.*.put(arena, context, request),
|
.AuthOK => callHandlerIfExist("options", self.ep, arena, context, request),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -312,7 +311,7 @@ pub fn Create(
|
||||||
pub fn head(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
pub fn head(self: *AuthenticatingEndpoint, arena: Allocator, context: *Context, request: zap.Request) anyerror!void {
|
||||||
try switch (self.authenticator.authenticateRequest(&request)) {
|
try switch (self.authenticator.authenticateRequest(&request)) {
|
||||||
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
.AuthFailed => return self.ep.*.unauthorized(arena, context, request),
|
||||||
.AuthOK => self.ep.*.head(arena, context, request),
|
.AuthOK => callHandlerIfExist("head", self.ep, arena, context, request),
|
||||||
.Handled => {},
|
.Handled => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -332,7 +331,7 @@ pub fn Create(
|
||||||
tls: ?zap.Tls = null,
|
tls: ?zap.Tls = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(gpa_: Allocator, context_: *Context, opts_: AppOpts) !App {
|
pub fn init(gpa_: Allocator, context_: *Context, opts_: AppOpts) !void {
|
||||||
if (_static.there_can_be_only_one) {
|
if (_static.there_can_be_only_one) {
|
||||||
return error.OnlyOneAppAllowed;
|
return error.OnlyOneAppAllowed;
|
||||||
}
|
}
|
||||||
|
@ -360,10 +359,9 @@ pub fn Create(
|
||||||
}
|
}
|
||||||
_static.unhandled_error = Context.unhandledError;
|
_static.unhandled_error = Context.unhandledError;
|
||||||
}
|
}
|
||||||
return .{};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(_: *App) void {
|
pub fn deinit() void {
|
||||||
// we created endpoint wrappers but only tracked their interfaces
|
// we created endpoint wrappers but only tracked their interfaces
|
||||||
// hence, we need to destroy the wrappers through their interfaces
|
// hence, we need to destroy the wrappers through their interfaces
|
||||||
if (false) {
|
if (false) {
|
||||||
|
@ -389,6 +387,15 @@ pub fn Create(
|
||||||
_static.track_arenas.deinit(_static.gpa);
|
_static.track_arenas.deinit(_static.gpa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This can be resolved at comptime so *perhaps it does affect optimiazation
|
||||||
|
pub fn callHandlerIfExist(comptime fn_name: []const u8, e: anytype, arena: Allocator, ctx: *Context, r: Request) anyerror!void {
|
||||||
|
const EndPoint = @TypeOf(e.*);
|
||||||
|
if (@hasDecl(EndPoint, fn_name)) {
|
||||||
|
return @field(EndPoint, fn_name)(e, arena, ctx, r);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_arena() !*ArenaAllocator {
|
pub fn get_arena() !*ArenaAllocator {
|
||||||
const thread_id = std.Thread.getCurrentId();
|
const thread_id = std.Thread.getCurrentId();
|
||||||
_static.track_arena_lock.lockShared();
|
_static.track_arena_lock.lockShared();
|
||||||
|
@ -411,7 +418,7 @@ pub fn Create(
|
||||||
/// If you try to register an endpoint whose path would shadow an
|
/// If you try to register an endpoint whose path would shadow an
|
||||||
/// already registered one, you will receive an
|
/// already registered one, you will receive an
|
||||||
/// EndpointPathShadowError.
|
/// EndpointPathShadowError.
|
||||||
pub fn register(_: *App, endpoint: anytype) !void {
|
pub fn register(endpoint: anytype) !void {
|
||||||
for (_static.endpoints.items) |other| {
|
for (_static.endpoints.items) |other| {
|
||||||
if (std.mem.startsWith(
|
if (std.mem.startsWith(
|
||||||
u8,
|
u8,
|
||||||
|
@ -432,7 +439,7 @@ pub fn Create(
|
||||||
try _static.endpoints.append(_static.gpa, &bound.interface);
|
try _static.endpoints.append(_static.gpa, &bound.interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen(_: *App, l: ListenerSettings) !void {
|
pub fn listen(l: ListenerSettings) !void {
|
||||||
_static.listener = HttpListener.init(.{
|
_static.listener = HttpListener.init(.{
|
||||||
.interface = l.interface,
|
.interface = l.interface,
|
||||||
.port = l.port,
|
.port = l.port,
|
||||||
|
|
Loading…
Add table
Reference in a new issue