Add way to specify route params
This commit is contained in:
parent
c19cc267bf
commit
789e9062b2
1 changed files with 59 additions and 31 deletions
|
@ -45,15 +45,19 @@ const PathIter = struct {
|
|||
}
|
||||
};
|
||||
|
||||
fn splitPath(comptime path: []const u8) []const []const u8 {
|
||||
fn splitRoutePath(comptime path: []const u8) []const RouteSegment {
|
||||
comptime {
|
||||
var segments: [path.len][]const u8 = undefined;
|
||||
var segments: [path.len]RouteSegment = undefined;
|
||||
|
||||
var iter = PathIter.from(path);
|
||||
var it = iter.next();
|
||||
var count = 0;
|
||||
while (it != null) : (it = iter.next()) {
|
||||
segments[count] = it.?;
|
||||
while (iter.next()) |it| {
|
||||
if (it[0] == ':') {
|
||||
segments[count] = .{ .param = it[1..] };
|
||||
} else {
|
||||
segments[count] = .{ .literal = it };
|
||||
}
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
||||
|
@ -61,17 +65,22 @@ fn splitPath(comptime path: []const u8) []const []const u8 {
|
|||
}
|
||||
}
|
||||
|
||||
const RouteSegment = union(enum) {
|
||||
literal: []const u8,
|
||||
param: []const u8,
|
||||
};
|
||||
|
||||
fn RouteWithContext(comptime Context: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
pub const Handler = fn (Context) void;
|
||||
|
||||
path_segments: []const []const u8,
|
||||
path_segments: []const RouteSegment,
|
||||
method: http.Method,
|
||||
handler: Handler,
|
||||
|
||||
pub fn bind(method: http.Method, comptime path: []const u8, handler: Handler) Self {
|
||||
return .{ .method = method, .path_segments = splitPath(path), .handler = handler };
|
||||
return .{ .method = method, .path_segments = splitRoutePath(path), .handler = handler };
|
||||
}
|
||||
|
||||
fn matchesPath(self: *const Self, request_path: []const u8) bool {
|
||||
|
@ -79,7 +88,12 @@ fn RouteWithContext(comptime Context: type) type {
|
|||
|
||||
for (self.path_segments) |route_seg| {
|
||||
const request_seg = request_segments.next() orelse return false;
|
||||
if (!ciutf8.eql(route_seg, request_seg)) return false;
|
||||
switch (route_seg) {
|
||||
.literal => |lit| {
|
||||
if (!ciutf8.eql(lit, request_seg)) return false;
|
||||
},
|
||||
.param => {},
|
||||
}
|
||||
}
|
||||
|
||||
return request_segments.next() == null;
|
||||
|
@ -136,35 +150,29 @@ const _tests = struct {
|
|||
try std.testing.expectEqual(@as(?[]const u8, null), it.next());
|
||||
}
|
||||
|
||||
fn expectEqualSegments(expected: []const []const u8, actual: []const []const u8) !void {
|
||||
fn expectEqualSegments(expected: []const RouteSegment, actual: []const RouteSegment) !void {
|
||||
try std.testing.expectEqual(expected.len, actual.len);
|
||||
for (expected) |_, i| {
|
||||
try std.testing.expectEqualStrings(expected[i], actual[i]);
|
||||
try std.testing.expectEqual(
|
||||
std.meta.activeTag(expected[i]),
|
||||
std.meta.activeTag(actual[i]),
|
||||
);
|
||||
switch (expected[i]) {
|
||||
.literal => |exp| try std.testing.expectEqualStrings(exp, actual[i].literal),
|
||||
.param => |exp| try std.testing.expectEqualStrings(exp, actual[i].param),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "splitPath" {
|
||||
const path = "//ab/c/de";
|
||||
const segments = splitPath(path);
|
||||
test "splitRoutePath" {
|
||||
const path = "//ab/c/:de/";
|
||||
const segments = splitRoutePath(path);
|
||||
|
||||
try expectEqualSegments(&[_][]const u8{ "ab", "c", "de" }, segments);
|
||||
}
|
||||
|
||||
fn expectEqualRoute(expected: RouteWithContext(Context), actual: RouteWithContext(Context)) !void {
|
||||
try std.testing.expectEqual(expected.method, actual.method);
|
||||
try std.testing.expectEqual(expected.handler, actual.handler);
|
||||
try expectEqualSegments(expected.path_segments, actual.path_segments);
|
||||
}
|
||||
|
||||
test "RouteWithContext(T).bind" {
|
||||
const R = RouteWithContext(Context);
|
||||
const r = R.bind(.GET, "//ab//cd", dummyHandler);
|
||||
|
||||
try expectEqualRoute(R{
|
||||
.method = .GET,
|
||||
.path_segments = &[_][]const u8{ "ab", "cd" },
|
||||
.handler = dummyHandler,
|
||||
}, r);
|
||||
try expectEqualSegments(&[_]RouteSegment{
|
||||
.{ .literal = "ab" },
|
||||
.{ .literal = "c" },
|
||||
.{ .param = "de" },
|
||||
}, segments);
|
||||
}
|
||||
|
||||
test "RouteWithContext(T).matchesPath" {
|
||||
|
@ -351,4 +359,24 @@ const _tests = struct {
|
|||
try mock_a.expectCalledOnceWith(12);
|
||||
try mock_404.expectNotCalled();
|
||||
}
|
||||
|
||||
test "Router(T).dispatch with variables" {
|
||||
const mock_a = CallTracker(.{}, dummyHandler);
|
||||
const mock_404 = CallTracker(.{}, dummyHandler);
|
||||
|
||||
const R = Router(Context).Route;
|
||||
const routes = [_]R{
|
||||
R.bind(.GET, "/test/:id/abcd", mock_a.func),
|
||||
};
|
||||
|
||||
const router = Router(Context){ .routes = &routes, .route_404 = mock_404.func };
|
||||
router.dispatch(.GET, "/test/lskdjflsdjfksld/abcd", 10);
|
||||
try mock_a.expectCalledOnceWith(10);
|
||||
try mock_404.expectNotCalled();
|
||||
mock_a.reset();
|
||||
|
||||
router.dispatch(.GET, "/test//abcd", 10);
|
||||
try mock_a.expectNotCalled();
|
||||
try mock_404.expectCalledOnceWith(10);
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue