1
0
Fork 0
mirror of https://github.com/zigzap/zap.git synced 2025-10-20 15:14:08 +00:00

Improved & generalized checkEndpoint functions

E.g. return type's error set does not need to be `anyerror` anymore.
This commit is contained in:
renerocksai 2025-03-30 19:37:40 +02:00
parent 793423b7c2
commit cc6d55fbf7
No known key found for this signature in database
5 changed files with 115 additions and 17 deletions

View file

@ -14,14 +14,14 @@ pub fn init(path: []const u8) StopEndpoint {
}; };
} }
pub fn get(e: *StopEndpoint, r: zap.Request) anyerror!void { pub fn get(e: *StopEndpoint, r: zap.Request) !void {
_ = e; _ = e;
_ = r; _ = r;
zap.stop(); zap.stop();
} }
pub fn post(_: *StopEndpoint, _: zap.Request) anyerror!void {} pub fn post(_: *StopEndpoint, _: zap.Request) !void {}
pub fn put(_: *StopEndpoint, _: zap.Request) anyerror!void {} pub fn put(_: *StopEndpoint, _: zap.Request) !void {}
pub fn delete(_: *StopEndpoint, _: zap.Request) anyerror!void {} pub fn delete(_: *StopEndpoint, _: zap.Request) !void {}
pub fn patch(_: *StopEndpoint, _: zap.Request) anyerror!void {} pub fn patch(_: *StopEndpoint, _: zap.Request) !void {}
pub fn options(_: *StopEndpoint, _: zap.Request) anyerror!void {} pub fn options(_: *StopEndpoint, _: zap.Request) !void {}

View file

@ -43,8 +43,9 @@ fn userIdFromPath(self: *UserWeb, path: []const u8) ?usize {
return null; return null;
} }
pub fn put(_: *UserWeb, _: zap.Request) anyerror!void {} pub fn put(_: *UserWeb, _: zap.Request) !void {}
pub fn get(self: *UserWeb, r: zap.Request) anyerror!void {
pub fn get(self: *UserWeb, r: zap.Request) !void {
if (r.path) |path| { if (r.path) |path| {
// /users // /users
if (path.len == self.path.len) { if (path.len == self.path.len) {
@ -69,7 +70,7 @@ fn listUsers(self: *UserWeb, r: zap.Request) !void {
} }
} }
pub fn post(self: *UserWeb, r: zap.Request) anyerror!void { pub fn post(self: *UserWeb, r: zap.Request) !void {
if (r.body) |body| { if (r.body) |body| {
const maybe_user: ?std.json.Parsed(User) = std.json.parseFromSlice(User, self.alloc, body, .{}) catch null; const maybe_user: ?std.json.Parsed(User) = std.json.parseFromSlice(User, self.alloc, body, .{}) catch null;
if (maybe_user) |u| { if (maybe_user) |u| {
@ -86,7 +87,7 @@ pub fn post(self: *UserWeb, r: zap.Request) anyerror!void {
} }
} }
pub fn patch(self: *UserWeb, r: zap.Request) anyerror!void { pub fn patch(self: *UserWeb, r: zap.Request) !void {
if (r.path) |path| { if (r.path) |path| {
if (self.userIdFromPath(path)) |id| { if (self.userIdFromPath(path)) |id| {
if (self._users.get(id)) |_| { if (self._users.get(id)) |_| {
@ -109,7 +110,7 @@ pub fn patch(self: *UserWeb, r: zap.Request) anyerror!void {
} }
} }
pub fn delete(self: *UserWeb, r: zap.Request) anyerror!void { pub fn delete(self: *UserWeb, r: zap.Request) !void {
if (r.path) |path| { if (r.path) |path| {
if (self.userIdFromPath(path)) |id| { if (self.userIdFromPath(path)) |id| {
var jsonbuf: [128]u8 = undefined; var jsonbuf: [128]u8 = undefined;
@ -124,7 +125,7 @@ pub fn delete(self: *UserWeb, r: zap.Request) anyerror!void {
} }
} }
pub fn options(_: *UserWeb, r: zap.Request) anyerror!void { pub fn options(_: *UserWeb, r: zap.Request) !void {
try r.setHeader("Access-Control-Allow-Origin", "*"); try r.setHeader("Access-Control-Allow-Origin", "*");
try r.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS"); try r.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
r.setStatus(zap.http.StatusCode.no_content); r.setStatus(zap.http.StatusCode.no_content);

View file

@ -1,3 +1,9 @@
//!
//! Part of the Zap examples.
//!
//! Build me with `zig build endpoint_auth`.
//! Run me with `zig build run-endpoint_auth`.
//!
const std = @import("std"); const std = @import("std");
const zap = @import("zap"); const zap = @import("zap");

View file

@ -153,12 +153,56 @@ pub fn Create(comptime Context: type) type {
"patch", "patch",
"options", "options",
}; };
const params_to_check = [_]type{
*T,
Allocator,
*Context,
Request,
};
inline for (methods_to_check) |method| { inline for (methods_to_check) |method| {
if (@hasDecl(T, method)) { if (@hasDecl(T, method)) {
const Method = @TypeOf(@field(T, method)); const Method = @TypeOf(@field(T, method));
const Expected = fn (_: *T, _: Allocator, _: *Context, _: Request) anyerror!void; const method_info = @typeInfo(Method);
if (Method != Expected) { if (method_info != .@"fn") {
@compileError(method ++ " method of " ++ @typeName(T) ++ " has wrong type:\n" ++ @typeName(Method) ++ "\nexpected:\n" ++ @typeName(Expected)); @compileError("Expected `" ++ @typeName(T) ++ "." ++ method ++ "` to be a request handler method, got: " ++ @typeName(Method));
}
// now check parameters
const params = method_info.@"fn".params;
if (params.len != params_to_check.len) {
@compileError(std.fmt.comptimePrint(
"Expected method `{s}.{s}` to have {d} parameters, got {d}",
.{
@typeName(T),
method,
params_to_check.len,
params.len,
},
));
}
inline for (params_to_check, 0..) |param_type_expected, i| {
if (params[i].type.? != param_type_expected) {
@compileError(std.fmt.comptimePrint(
"Expected parameter {d} of method {s}.{s} to be {s}, got {s}",
.{
i + 1,
@typeName(T),
method,
@typeName(param_type_expected),
@typeName(params[i].type.?),
},
));
}
}
const ret_type = method_info.@"fn".return_type.?;
const ret_info = @typeInfo(ret_type);
if (ret_info != .error_union) {
@compileError("Expected return type of method `" ++ @typeName(T) ++ "." ++ method ++ "` to be !void, got: " ++ @typeName(ret_type));
}
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));
} }
} else { } else {
@compileError(@typeName(T) ++ " has no method named `" ++ method ++ "`"); @compileError(@typeName(T) ++ " has no method named `" ++ method ++ "`");

View file

@ -95,10 +95,57 @@ pub fn checkEndpointType(T: type) void {
"patch", "patch",
"options", "options",
}; };
const params_to_check = [_]type{
*T,
Request,
};
inline for (methods_to_check) |method| { inline for (methods_to_check) |method| {
if (@hasDecl(T, method)) { if (@hasDecl(T, method)) {
if (@TypeOf(@field(T, method)) != fn (_: *T, _: Request) anyerror!void) { const Method = @TypeOf(@field(T, method));
@compileError(method ++ " method of " ++ @typeName(T) ++ " has wrong type:\n" ++ @typeName(@TypeOf(T.get)) ++ "\nexpected:\n" ++ @typeName(fn (_: *T, _: Request) anyerror!void)); const method_info = @typeInfo(Method);
if (method_info != .@"fn") {
@compileError("Expected `" ++ @typeName(T) ++ "." ++ method ++ "` to be a request handler method, got: " ++ @typeName(Method));
}
// now check parameters
const params = method_info.@"fn".params;
if (params.len != params_to_check.len) {
@compileError(std.fmt.comptimePrint(
"Expected method `{s}.{s}` to have {d} parameters, got {d}",
.{
@typeName(T),
method,
params_to_check.len,
params.len,
},
));
}
inline for (params_to_check, 0..) |param_type_expected, i| {
if (params[i].type.? != param_type_expected) {
@compileError(std.fmt.comptimePrint(
"Expected parameter {d} of method {s}.{s} to be {s}, got {s}",
.{
i + 1,
@typeName(T),
method,
@typeName(param_type_expected),
@typeName(params[i].type.?),
},
));
}
}
// check return type
const ret_type = method_info.@"fn".return_type.?;
const ret_info = @typeInfo(ret_type);
if (ret_info != .error_union) {
@compileError("Expected return type of method `" ++ @typeName(T) ++ "." ++ method ++ "` to be !void, got: " ++ @typeName(ret_type));
}
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));
} }
} else { } else {
@compileError(@typeName(T) ++ " has no method named `" ++ method ++ "`"); @compileError(@typeName(T) ++ " has no method named `" ++ method ++ "`");