basic test (failing)

This commit is contained in:
Jeeves 2024-03-26 11:23:48 -06:00
parent 89f036f9c5
commit 6925d69bac

View file

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