1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
const std = @import("std");
const Route = @import("Route.zig");
repr: []const u8,
pub fn as(self: @This(), T: type) (if (T == []const u8) T else anyerror!T) {
if (T == []const u8) {
return self.repr;
} else if (@typeInfo(T) == .int) {
return std.fmt.parseInt(T, self.repr, 10);
}
@compileError(std.fmt.comptimePrint("unsupported default type '{}'", .{T}));
}
pub fn convert(self: @This(), func: anytype) (@typeInfo(@TypeOf(func)).@"fn".return_type orelse void) {
return func(self.repr);
}
pub fn Struct(comptime route: Route) type {
var names: []const []const u8 = &.{};
inline for (route.segments) |segment| {
switch (segment) {
.literal => {},
.identifier => |id| {
const name: []const []const u8 = &.{id};
names = names ++ name;
},
}
}
var types: [names.len]type = undefined;
var attrs: [names.len]std.builtin.Type.StructField.Attributes = undefined;
@memset(&types, @This());
@memset(&attrs, std.builtin.Type.StructField.Attributes{});
return @Struct(.auto, null, names, &types, &attrs);
}
pub fn parse(P: type, comptime route: *const Route, url: []const u8) P {
var params: P = undefined;
var buffer = url;
inline for (route.segments) |segment| {
buffer = std.mem.trimStart(u8, buffer, "/");
const index = std.mem.indexOfScalar(u8, buffer, '/') orelse buffer.len;
const value = buffer[0..index];
buffer = buffer[index..];
switch (segment) {
.literal => {},
.identifier => |id| {
@field(params, id) = .{ .repr = value };
},
}
}
return params;
}
test "parse" {
const route: Route = comptime .fromAny(.{ "api", "users", .name, "settings", .id });
const Params = Struct(route);
const url = "/api/users/nathan.reiner/settings/123456789";
const params = parse(Params, &route, url);
try std.testing.expectEqualStrings("nathan.reiner", params.name.as([]const u8));
try std.testing.expectEqual(123456789, try params.id.as(usize));
}
|