basic test (failing)
This commit is contained in:
parent
89f036f9c5
commit
6925d69bac
1 changed files with 77 additions and 18 deletions
95
src/main.zig
95
src/main.zig
|
@ -139,32 +139,36 @@ pub const Listener = struct {
|
||||||
pub const Router = struct {
|
pub const Router = struct {
|
||||||
allocator: mem.Allocator,
|
allocator: mem.Allocator,
|
||||||
|
|
||||||
root_node: Node,
|
root_node: *Node,
|
||||||
static_routes: std.StringHashMap(*Node),
|
static_routes: std.StringHashMap(*Node),
|
||||||
|
|
||||||
pub fn init(allocator: mem.Allocator, root_handler: HandlerFn) Router {
|
pub fn init(allocator: mem.Allocator, root_handler: HandlerFn) Router {
|
||||||
|
var node = Node.init(allocator) catch @panic("OOM");
|
||||||
|
node.handler = root_handler;
|
||||||
return .{
|
return .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.root_node = .{
|
.root_node = node,
|
||||||
.type = .normal,
|
// .root_node = .{
|
||||||
.max_depth = 1024,
|
// .type = .normal,
|
||||||
.children = std.StringHashMap(*Node).init(allocator),
|
// .max_depth = 1024,
|
||||||
.handler = root_handler,
|
// .children = std.StringHashMap(*Node).init(allocator),
|
||||||
.placeholder_children = std.ArrayList(*Node).init(allocator),
|
// .handler = root_handler,
|
||||||
},
|
// .placeholder_children = std.ArrayList(*Node).init(allocator),
|
||||||
|
// },
|
||||||
.static_routes = std.StringHashMap(*Node).init(allocator),
|
.static_routes = std.StringHashMap(*Node).init(allocator),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Router) void {
|
pub fn deinit(self: *Router) void {
|
||||||
self.root_node.children.deinit();
|
self.root_node.deinit(self.allocator);
|
||||||
self.root_node.placeholder_children.deinit();
|
// self.root_node.children.deinit();
|
||||||
|
// self.root_node.placeholder_children.deinit();
|
||||||
self.static_routes.deinit();
|
self.static_routes.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle(self: *Router, event: *Listener.Event) !void {
|
pub fn handle(self: *Router, event: *Listener.Event) !void {
|
||||||
const handlerFn = try self.getRoute(event.req.uri.path);
|
const route = try self.getRoute(event.req.uri.path);
|
||||||
try handlerFn(event);
|
try route.handler(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a route if the path is not already present, otherwise overwrite preexisting HandlerFn.
|
/// Insert a route if the path is not already present, otherwise overwrite preexisting HandlerFn.
|
||||||
|
@ -172,7 +176,7 @@ pub const Listener = struct {
|
||||||
// std.debug.print("\npath {s}\n", .{path});
|
// std.debug.print("\npath {s}\n", .{path});
|
||||||
var is_static_route = true;
|
var is_static_route = true;
|
||||||
var sections = mem.splitScalar(u8, path, '/');
|
var sections = mem.splitScalar(u8, path, '/');
|
||||||
var node = &self.root_node;
|
var node = self.root_node;
|
||||||
var unnamed_placeholder_ctr: usize = 0;
|
var unnamed_placeholder_ctr: usize = 0;
|
||||||
var matched_nodes = std.ArrayList(*Node).init(self.allocator);
|
var matched_nodes = std.ArrayList(*Node).init(self.allocator);
|
||||||
defer matched_nodes.deinit();
|
defer matched_nodes.deinit();
|
||||||
|
@ -226,14 +230,14 @@ pub const Listener = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the HandlerFn associated with path, otherwise get root_handler.
|
/// Get the HandlerFn associated with path, otherwise get root_handler.
|
||||||
pub fn getRoute(self: *Router, path: []const u8) !HandlerFn {
|
pub fn getRoute(self: *Router, path: []const u8) !*Node {
|
||||||
if (self.static_routes.get(path)) |rt| return rt.handler;
|
if (self.static_routes.get(path)) |rt| return rt;
|
||||||
|
|
||||||
var params = std.StringHashMap([]const u8).init(self.allocator);
|
var params = std.StringHashMap([]const u8).init(self.allocator);
|
||||||
defer params.deinit();
|
defer params.deinit();
|
||||||
var params_found = false;
|
var params_found = false;
|
||||||
var wildcard_node: ?*Node = null;
|
var wildcard_node: ?*Node = null;
|
||||||
var node: *Node = &self.root_node;
|
var node: *Node = self.root_node;
|
||||||
var wildcard_param: ?[]const u8 = null;
|
var wildcard_param: ?[]const u8 = null;
|
||||||
|
|
||||||
var remaining = mem.count(u8, path, "/") + 1;
|
var remaining = mem.count(u8, path, "/") + 1;
|
||||||
|
@ -277,10 +281,36 @@ pub const Listener = struct {
|
||||||
params_found = true;
|
params_found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.root_node.handler;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Node = struct {
|
/// If there is a route with a matching path, it is deleted from the router, and this function return true. Otherwise it returns false.
|
||||||
|
pub fn removeRoute(self: *Router, path: []const u8) bool {
|
||||||
|
if (self.static_routes.remove(path)) return true;
|
||||||
|
|
||||||
|
var opt_node: ?*Node = self.root_node;
|
||||||
|
var sections = mem.splitScalar(u8, path, '/');
|
||||||
|
while (sections.next()) |section| {
|
||||||
|
// TODO: reorder to be safe
|
||||||
|
opt_node = opt_node.?.children.get(section);
|
||||||
|
if (opt_node == null) return false;
|
||||||
|
}
|
||||||
|
// TODO: should this node.parent be an assert instead?
|
||||||
|
if (opt_node) |node| {
|
||||||
|
if (node.children.count() == 0) if (node.parent) |parent| {
|
||||||
|
var rest_sections = mem.splitScalar(u8, sections.rest(), '/');
|
||||||
|
while (rest_sections.peek()) |_| _ = rest_sections.next();
|
||||||
|
const last_section = rest_sections.next().?;
|
||||||
|
parent.wildcard_child_node = null;
|
||||||
|
parent.placeholder_children.clearAndFree();
|
||||||
|
_ = parent.children.remove(last_section); // TODO assert this is true
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Node = struct {
|
||||||
type: Type,
|
type: Type,
|
||||||
max_depth: usize, // TODO: what is best here
|
max_depth: usize, // TODO: what is best here
|
||||||
parent: ?*Node = null,
|
parent: ?*Node = null,
|
||||||
|
@ -302,10 +332,39 @@ pub const Listener = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Node, allocator: mem.Allocator) void {
|
pub fn deinit(self: *Node, allocator: mem.Allocator) void {
|
||||||
|
for (self.placeholder_children.items) |child| child.deinit(allocator);
|
||||||
self.placeholder_children.deinit();
|
self.placeholder_children.deinit();
|
||||||
|
var child_it = self.children.valueIterator();
|
||||||
|
while (child_it.next()) |child| child.*.deinit(allocator);
|
||||||
self.children.deinit();
|
self.children.deinit();
|
||||||
allocator.destroy(self);
|
allocator.destroy(self);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
test "Router" {
|
||||||
|
var router = Listener.Router.init(std.testing.allocator, &dummyHandler);
|
||||||
|
defer router.deinit();
|
||||||
|
|
||||||
|
try router.putRoute("/", &hummyDandler);
|
||||||
|
try router.putRoute("/foo", &hummyDandler);
|
||||||
|
try router.putRoute("/foo/bar", &hummyDandler);
|
||||||
|
try router.putRoute("/foo/foobar", &hummyDandler);
|
||||||
|
try router.putRoute("/bar", &hummyDandler);
|
||||||
|
|
||||||
|
try std.testing.expectEqual(&hummyDandler, (try router.getRoute("/")).handler);
|
||||||
|
try std.testing.expectEqual(&hummyDandler, (try router.getRoute("/foo")).handler);
|
||||||
|
try std.testing.expectEqual(&hummyDandler, (try router.getRoute("/foo/bar")).handler);
|
||||||
|
try std.testing.expectEqual(&hummyDandler, (try router.getRoute("/foo/foobar")).handler);
|
||||||
|
try std.testing.expectEqual(&hummyDandler, (try router.getRoute("/bar")).handler);
|
||||||
|
|
||||||
|
try std.testing.expect(router.removeRoute("/foo"));
|
||||||
|
|
||||||
|
try std.testing.expectEqual(&dummyHandler, (try router.getRoute("/foo/bar")).handler);
|
||||||
|
try std.testing.expectEqual(&dummyHandler, (try router.getRoute("/foo")).handler);
|
||||||
|
try std.testing.expectEqual(&hummyDandler, (try router.getRoute("/")).handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dummyHandler(_: *Listener.Event) anyerror!void {}
|
||||||
|
fn hummyDandler(_: *Listener.Event) anyerror!void {}
|
||||||
|
|
Loading…
Add table
Reference in a new issue