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 {
|
comptime {
|
||||||
var segments: [path.len][]const u8 = undefined;
|
var segments: [path.len]RouteSegment = undefined;
|
||||||
|
|
||||||
var iter = PathIter.from(path);
|
var iter = PathIter.from(path);
|
||||||
var it = iter.next();
|
|
||||||
var count = 0;
|
var count = 0;
|
||||||
while (it != null) : (it = iter.next()) {
|
while (iter.next()) |it| {
|
||||||
segments[count] = it.?;
|
if (it[0] == ':') {
|
||||||
|
segments[count] = .{ .param = it[1..] };
|
||||||
|
} else {
|
||||||
|
segments[count] = .{ .literal = it };
|
||||||
|
}
|
||||||
|
|
||||||
count += 1;
|
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 {
|
fn RouteWithContext(comptime Context: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
pub const Handler = fn (Context) void;
|
pub const Handler = fn (Context) void;
|
||||||
|
|
||||||
path_segments: []const []const u8,
|
path_segments: []const RouteSegment,
|
||||||
method: http.Method,
|
method: http.Method,
|
||||||
handler: Handler,
|
handler: Handler,
|
||||||
|
|
||||||
pub fn bind(method: http.Method, comptime path: []const u8, handler: Handler) Self {
|
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 {
|
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| {
|
for (self.path_segments) |route_seg| {
|
||||||
const request_seg = request_segments.next() orelse return false;
|
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;
|
return request_segments.next() == null;
|
||||||
|
@ -136,35 +150,29 @@ const _tests = struct {
|
||||||
try std.testing.expectEqual(@as(?[]const u8, null), it.next());
|
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);
|
try std.testing.expectEqual(expected.len, actual.len);
|
||||||
for (expected) |_, i| {
|
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" {
|
test "splitRoutePath" {
|
||||||
const path = "//ab/c/de";
|
const path = "//ab/c/:de/";
|
||||||
const segments = splitPath(path);
|
const segments = splitRoutePath(path);
|
||||||
|
|
||||||
try expectEqualSegments(&[_][]const u8{ "ab", "c", "de" }, segments);
|
try expectEqualSegments(&[_]RouteSegment{
|
||||||
}
|
.{ .literal = "ab" },
|
||||||
|
.{ .literal = "c" },
|
||||||
fn expectEqualRoute(expected: RouteWithContext(Context), actual: RouteWithContext(Context)) !void {
|
.{ .param = "de" },
|
||||||
try std.testing.expectEqual(expected.method, actual.method);
|
}, segments);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "RouteWithContext(T).matchesPath" {
|
test "RouteWithContext(T).matchesPath" {
|
||||||
|
@ -351,4 +359,24 @@ const _tests = struct {
|
||||||
try mock_a.expectCalledOnceWith(12);
|
try mock_a.expectCalledOnceWith(12);
|
||||||
try mock_404.expectNotCalled();
|
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