179 lines
4.7 KiB
Zig
179 lines
4.7 KiB
Zig
|
const std = @import("std");
|
||
|
|
||
|
pub fn Separator(comptime separator: u8) type {
|
||
|
return struct {
|
||
|
const Self = @This();
|
||
|
str: []const u8,
|
||
|
pub fn from(str: []const u8) Self {
|
||
|
return .{ .str = std.mem.trim(u8, str, &.{separator}) };
|
||
|
}
|
||
|
|
||
|
pub fn next(self: *Self) ?[]const u8 {
|
||
|
if (self.str.len == 0) return null;
|
||
|
|
||
|
const part = std.mem.sliceTo(self.str, separator);
|
||
|
self.str = std.mem.trimLeft(u8, self.str[part.len..], &.{separator});
|
||
|
|
||
|
return part;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
pub const QueryIter = struct {
|
||
|
const Pair = struct {
|
||
|
key: []const u8,
|
||
|
value: ?[]const u8,
|
||
|
};
|
||
|
|
||
|
iter: Separator('&'),
|
||
|
|
||
|
pub fn from(q: []const u8) QueryIter {
|
||
|
return QueryIter{ .iter = Separator('&').from(std.mem.trimLeft(u8, q, "?")) };
|
||
|
}
|
||
|
|
||
|
pub fn next(self: *QueryIter) ?Pair {
|
||
|
const part = self.iter.next() orelse return null;
|
||
|
|
||
|
const key = std.mem.sliceTo(part, '=');
|
||
|
if (key.len == part.len) return Pair{
|
||
|
.key = key,
|
||
|
.value = null,
|
||
|
};
|
||
|
|
||
|
return Pair{
|
||
|
.key = key,
|
||
|
.value = part[key.len + 1 ..],
|
||
|
};
|
||
|
}
|
||
|
};
|
||
|
|
||
|
pub const PathIter = struct {
|
||
|
is_first: bool,
|
||
|
iter: Separator('/'),
|
||
|
|
||
|
pub fn from(path: []const u8) PathIter {
|
||
|
return .{ .is_first = true, .iter = Separator('/').from(path) };
|
||
|
}
|
||
|
|
||
|
pub fn next(self: *PathIter) ?[]const u8 {
|
||
|
if (self.is_first) {
|
||
|
self.is_first = false;
|
||
|
return self.iter.next() orelse "";
|
||
|
}
|
||
|
|
||
|
return self.iter.next();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
test "QueryIter" {
|
||
|
const t = @import("std").testing;
|
||
|
if (true) return error.SkipZigTest;
|
||
|
{
|
||
|
var iter = QueryIter.from("");
|
||
|
try t.expect(iter.next() == null);
|
||
|
try t.expect(iter.next() == null);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var iter = QueryIter.from("?");
|
||
|
try t.expect(iter.next() == null);
|
||
|
try t.expect(iter.next() == null);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var iter = QueryIter.from("?abc");
|
||
|
try t.expectEqual(QueryIter.Pair{
|
||
|
.key = "abc",
|
||
|
.value = null,
|
||
|
}, iter.next().?);
|
||
|
try t.expect(iter.next() == null);
|
||
|
try t.expect(iter.next() == null);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var iter = QueryIter.from("?abc=");
|
||
|
try t.expectEqual(QueryIter.Pair{
|
||
|
.key = "abc",
|
||
|
.value = "",
|
||
|
}, iter.next().?);
|
||
|
try t.expect(iter.next() == null);
|
||
|
try t.expect(iter.next() == null);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var iter = QueryIter.from("?abc=def");
|
||
|
try t.expectEqual(QueryIter.Pair{
|
||
|
.key = "abc",
|
||
|
.value = "def",
|
||
|
}, iter.next().?);
|
||
|
try t.expect(iter.next() == null);
|
||
|
try t.expect(iter.next() == null);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var iter = QueryIter.from("?abc=def&");
|
||
|
try t.expectEqual(QueryIter.Pair{
|
||
|
.key = "abc",
|
||
|
.value = "def",
|
||
|
}, iter.next().?);
|
||
|
try t.expect(iter.next() == null);
|
||
|
try t.expect(iter.next() == null);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var iter = QueryIter.from("?abc=def&foo&bar=baz&qux=");
|
||
|
try t.expectEqual(QueryIter.Pair{
|
||
|
.key = "abc",
|
||
|
.value = "def",
|
||
|
}, iter.next().?);
|
||
|
try t.expectEqual(QueryIter.Pair{
|
||
|
.key = "foo",
|
||
|
.value = null,
|
||
|
}, iter.next().?);
|
||
|
try t.expectEqual(QueryIter.Pair{
|
||
|
.key = "bar",
|
||
|
.value = "baz",
|
||
|
}, iter.next().?);
|
||
|
try t.expectEqual(QueryIter.Pair{
|
||
|
.key = "qux",
|
||
|
.value = "",
|
||
|
}, iter.next().?);
|
||
|
try t.expect(iter.next() == null);
|
||
|
try t.expect(iter.next() == null);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
var iter = QueryIter.from("?=def&");
|
||
|
try t.expectEqual(QueryIter.Pair{
|
||
|
.key = "",
|
||
|
.value = "def",
|
||
|
}, iter.next().?);
|
||
|
try t.expect(iter.next() == null);
|
||
|
try t.expect(iter.next() == null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
test "PathIter /ab/cd/" {
|
||
|
const path = "/ab/cd/";
|
||
|
var it = PathIter.from(path);
|
||
|
try std.testing.expectEqualStrings("ab", it.next().?);
|
||
|
try std.testing.expectEqualStrings("cd", it.next().?);
|
||
|
try std.testing.expectEqual(@as(?[]const u8, null), it.next());
|
||
|
}
|
||
|
|
||
|
test "PathIter ''" {
|
||
|
const path = "";
|
||
|
var it = PathIter.from(path);
|
||
|
try std.testing.expectEqualStrings("", it.next().?);
|
||
|
try std.testing.expectEqual(@as(?[]const u8, null), it.next());
|
||
|
}
|
||
|
|
||
|
test "PathIter ab/c//defg/" {
|
||
|
const path = "ab/c//defg/";
|
||
|
var it = PathIter.from(path);
|
||
|
try std.testing.expectEqualStrings("ab", it.next().?);
|
||
|
try std.testing.expectEqualStrings("c", it.next().?);
|
||
|
try std.testing.expectEqualStrings("defg", it.next().?);
|
||
|
try std.testing.expectEqual(@as(?[]const u8, null), it.next());
|
||
|
}
|