mirror of
https://github.com/zigzap/zap.git
synced 2025-10-21 07:34:08 +00:00
re-write of zap.Router, fix #83
This commit is contained in:
parent
135827977a
commit
c81a211bd3
3 changed files with 74 additions and 34 deletions
|
@ -82,17 +82,17 @@ pub fn main() !void {
|
||||||
|
|
||||||
var somePackage = SomePackage.init(allocator, 1, 2);
|
var somePackage = SomePackage.init(allocator, 1, 2);
|
||||||
|
|
||||||
try simpleRouter.handle_func("/", on_request_verbose);
|
try simpleRouter.handle_func_unbound("/", on_request_verbose);
|
||||||
|
|
||||||
try simpleRouter.handle_func("/geta", zap.RequestHandler(&somePackage, SomePackage.getA));
|
try simpleRouter.handle_func("/geta", &somePackage, &SomePackage.getA);
|
||||||
|
|
||||||
try simpleRouter.handle_func("/getb", zap.RequestHandler(&somePackage, SomePackage.getB));
|
try simpleRouter.handle_func("/getb", &somePackage, &SomePackage.getB);
|
||||||
|
|
||||||
try simpleRouter.handle_func("/inca", zap.RequestHandler(&somePackage, SomePackage.incrementA));
|
try simpleRouter.handle_func("/inca", &somePackage, &SomePackage.incrementA);
|
||||||
|
|
||||||
var listener = zap.HttpListener.init(.{
|
var listener = zap.HttpListener.init(.{
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
.on_request = zap.RequestHandler(&simpleRouter, &zap.Router.serve),
|
.on_request = simpleRouter.on_request_handler(),
|
||||||
.log = true,
|
.log = true,
|
||||||
.max_clients = 100000,
|
.max_clients = 100000,
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,6 +2,8 @@ const std = @import("std");
|
||||||
const zap = @import("zap.zig");
|
const zap = @import("zap.zig");
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
/// Errors returnable by init()
|
||||||
const RouterError = error{
|
const RouterError = error{
|
||||||
AlreadyExists,
|
AlreadyExists,
|
||||||
EmptyPath,
|
EmptyPath,
|
||||||
|
@ -9,46 +11,101 @@ const RouterError = error{
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
/// This is a singleton
|
||||||
|
var _instance: *Self = undefined;
|
||||||
|
|
||||||
|
/// Options to pass to init()
|
||||||
pub const Options = struct {
|
pub const Options = struct {
|
||||||
|
/// an optional zap request function for 404 not found case
|
||||||
not_found: ?zap.HttpRequestFn = null,
|
not_found: ?zap.HttpRequestFn = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
routes: std.StringHashMap(zap.HttpRequestFn),
|
const CallbackTag = enum { bound, unbound };
|
||||||
|
const BoundHandler = *fn (*const anyopaque, zap.Request) void;
|
||||||
|
const Callback = union(CallbackTag) {
|
||||||
|
bound: struct { instance: usize, handler: usize },
|
||||||
|
unbound: zap.HttpRequestFn,
|
||||||
|
};
|
||||||
|
|
||||||
|
routes: std.StringHashMap(Callback),
|
||||||
not_found: ?zap.HttpRequestFn,
|
not_found: ?zap.HttpRequestFn,
|
||||||
|
|
||||||
|
/// Create a new Router
|
||||||
pub fn init(allocator: Allocator, options: Options) Self {
|
pub fn init(allocator: Allocator, options: Options) Self {
|
||||||
return .{
|
return .{
|
||||||
.routes = std.StringHashMap(zap.HttpRequestFn).init(allocator),
|
.routes = std.StringHashMap(Callback).init(allocator),
|
||||||
|
|
||||||
.not_found = options.not_found,
|
.not_found = options.not_found,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deinit the router
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
self.routes.deinit();
|
self.routes.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_func(self: *Self, path: []const u8, h: zap.HttpRequestFn) !void {
|
/// Call this to add a route with an unbound handler: a handler that is not member of a struct.
|
||||||
|
pub fn handle_func_unbound(self: *Self, path: []const u8, h: zap.HttpRequestFn) !void {
|
||||||
if (path.len == 0) {
|
if (path.len == 0) {
|
||||||
return RouterError.EmptyPath;
|
return RouterError.EmptyPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
const route = self.routes.get(path);
|
if (self.routes.contains(path)) {
|
||||||
|
|
||||||
if (route != null) {
|
|
||||||
return RouterError.AlreadyExists;
|
return RouterError.AlreadyExists;
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.routes.put(path, h);
|
try self.routes.put(path, Callback{ .unbound = h });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve(self: *Self, r: zap.Request) void {
|
/// Call this to add a route with a handler that is bound to an instance of a struct.
|
||||||
const path = if (r.path) |p| p else "/";
|
/// Example:
|
||||||
|
///
|
||||||
|
/// ```zig
|
||||||
|
/// const HandlerType = struct {
|
||||||
|
/// pub fn getA(self: *HandlerType, r: zap.Request) void {
|
||||||
|
/// _ = self;
|
||||||
|
/// r.sendBody("hello\n\n") catch return;
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// var handler_instance = HandlerType{};
|
||||||
|
///
|
||||||
|
/// my_router.handle_func("/getA", &handler_instance, HandlerType.getA);
|
||||||
|
/// ```
|
||||||
|
pub fn handle_func(self: *Self, path: []const u8, instance: *anyopaque, handler: anytype) !void {
|
||||||
|
// TODO: assert type of instance has handler
|
||||||
|
|
||||||
var route = self.routes.get(path);
|
if (path.len == 0) {
|
||||||
|
return RouterError.EmptyPath;
|
||||||
|
}
|
||||||
|
|
||||||
if (route) |handler| {
|
if (self.routes.contains(path)) {
|
||||||
handler(r);
|
return RouterError.AlreadyExists;
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.routes.put(path, Callback{ .bound = .{
|
||||||
|
.instance = @intFromPtr(instance),
|
||||||
|
.handler = @intFromPtr(handler),
|
||||||
|
} });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the zap request handler function needed for a listener
|
||||||
|
pub fn on_request_handler(self: *Self) zap.HttpRequestFn {
|
||||||
|
_instance = self;
|
||||||
|
return zap_on_request;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zap_on_request(r: zap.Request) void {
|
||||||
|
return serve(_instance, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serve(self: *Self, r: zap.Request) void {
|
||||||
|
const path = r.path orelse "/";
|
||||||
|
|
||||||
|
if (self.routes.get(path)) |routeInfo| {
|
||||||
|
switch (routeInfo) {
|
||||||
|
.bound => |b| @call(.auto, @as(BoundHandler, @ptrFromInt(b.handler)), .{ @as(*anyopaque, @ptrFromInt(b.instance)), r }),
|
||||||
|
.unbound => |h| h(r),
|
||||||
|
}
|
||||||
} else if (self.not_found) |handler| {
|
} else if (self.not_found) |handler| {
|
||||||
// not found handler
|
// not found handler
|
||||||
handler(r);
|
handler(r);
|
||||||
|
|
17
src/util.zig
17
src/util.zig
|
@ -2,23 +2,6 @@ const std = @import("std");
|
||||||
const fio = @import("fio.zig");
|
const fio = @import("fio.zig");
|
||||||
const zap = @import("zap.zig");
|
const zap = @import("zap.zig");
|
||||||
|
|
||||||
/// capture self for RequestFn signature support
|
|
||||||
pub inline fn RequestHandler(self: anytype, func: *const fn (@TypeOf(self), zap.Request) void) *const fn (zap.Request) void {
|
|
||||||
return (opaque {
|
|
||||||
var hidden_self: @TypeOf(self) = undefined;
|
|
||||||
var hidden_func: *const fn (@TypeOf(self), zap.Request) void = undefined;
|
|
||||||
pub fn init(h_self: @TypeOf(self), h_func: *const fn (@TypeOf(self), zap.Request) void) *const @TypeOf(run) {
|
|
||||||
hidden_self = h_self;
|
|
||||||
hidden_func = h_func;
|
|
||||||
return &run;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(req: zap.Request) void {
|
|
||||||
hidden_func(hidden_self, req);
|
|
||||||
}
|
|
||||||
}).init(self, func);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used internally: convert a FIO object into its string representation.
|
/// Used internally: convert a FIO object into its string representation.
|
||||||
/// note: since this is called from within request functions, we don't make
|
/// note: since this is called from within request functions, we don't make
|
||||||
/// copies. Also, we return temp memory from fio. -> don't hold on to it outside
|
/// copies. Also, we return temp memory from fio. -> don't hold on to it outside
|
||||||
|
|
Loading…
Add table
Reference in a new issue