const std = @import("std"); const router = @import("../../http/router.zig"); pub const Endpoint = router.Endpoint; pub const endpoints = router.endpoints; pub const group_order = [_][]const u8{ "zds", "status", "discovery", "oauth", "server", "account", "preferences", "repo", "sync", "identity", "space", }; pub fn groupCount(group: []const u8) usize { var count: usize = 0; for (endpoints) |endpoint| { if (std.mem.eql(u8, endpoint.group, group)) count += 1; } return count; } pub fn groupCountTotal() usize { var count: usize = 0; for (group_order) |group| { if (groupCount(group) > 0) count += 1; } return count; } pub fn xrpcCount() usize { var count: usize = 0; for (endpoints) |endpoint| { if (endpoint.nsid() != null) count += 1; } return count; } pub fn experimentalCount() usize { var count: usize = 0; for (endpoints) |endpoint| { if (isExperimental(endpoint)) count += 1; } return count; } pub fn isExperimental(endpoint: Endpoint) bool { return std.mem.eql(u8, endpoint.group, "space"); } pub fn pathSeenBefore(index: usize) bool { for (endpoints[0..index]) |endpoint| { if (std.mem.eql(u8, endpoint.path, endpoints[index].path)) return true; } return false; } pub fn lower(allocator: std.mem.Allocator, input: []const u8) ![]const u8 { const out = try allocator.alloc(u8, input.len); for (input, 0..) |c, i| out[i] = std.ascii.toLower(c); return out; } pub fn methodLower(input: []const u8) []const u8 { if (std.mem.eql(u8, input, "GET")) return "get"; if (std.mem.eql(u8, input, "HEAD")) return "head"; if (std.mem.eql(u8, input, "POST")) return "post"; return input; } pub fn escapeHtml(allocator: std.mem.Allocator, input: []const u8) ![]const u8 { var out: std.Io.Writer.Allocating = .init(allocator); defer out.deinit(); for (input) |c| { switch (c) { '&' => try out.writer.writeAll("&"), '<' => try out.writer.writeAll("<"), '>' => try out.writer.writeAll(">"), '"' => try out.writer.writeAll("""), '\'' => try out.writer.writeAll("'"), else => try out.writer.writeByte(c), } } return out.toOwnedSlice(); } test "endpoint inventory covers XRPC paths" { try std.testing.expect(endpoints.len > 50); try std.testing.expect(xrpcCount() > 30); try std.testing.expect(experimentalCount() > 0); for (endpoints) |endpoint| { if (endpoint.nsid()) |id| { try std.testing.expect(id.len > 0); try std.testing.expect(std.mem.indexOfScalar(u8, id, '/') == null); } } }