aboutsummaryrefslogtreecommitdiff
path: root/src/http/Parameter.zig
blob: 03260b227f1587a277d5636315bf7dabcb570a4f (plain)
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));
}