fediglam/src/util/iters.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());
}