this repo has no description
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

did: add did parsing and validation

+182 -1
+179
src/did.zig
··· 1 + const std = @import("std"); 2 + 3 + const Allocator = std.mem.Allocator; 4 + 5 + pub const DidError = error{ 6 + InvalidDid, 7 + UnsupportedMethod, 8 + } || Allocator.Error; 9 + 10 + pub const Method = enum { 11 + plc, 12 + web, 13 + }; 14 + 15 + pub const Did = union(Method) { 16 + plc: [24]u8, 17 + web: Web, 18 + 19 + pub fn init(gpa: Allocator, bytes: []const u8) DidError!Did { 20 + var iter = std.mem.splitScalar(u8, bytes, ':'); 21 + if (!std.mem.eql(u8, iter.first(), "did")) { 22 + return error.InvalidDid; 23 + } 24 + 25 + const kind_str = iter.next() orelse return error.InvalidDid; 26 + const kind = std.meta.stringToEnum(Method, kind_str) orelse 27 + return error.UnsupportedMethod; 28 + 29 + switch (kind) { 30 + .plc => { 31 + const id = iter.next() orelse return error.InvalidDid; 32 + if (id.len != 24) return error.InvalidDid; 33 + var did: [24]u8 = undefined; 34 + for (id, 0..) |c, i| { 35 + switch (c) { 36 + 'A'...'Z' => did[i] = c + 0x20, 37 + 'a'...'z', '2'...'7' => did[i] = c, 38 + 39 + else => return error.InvalidDid, 40 + } 41 + } 42 + return .{ .plc = did }; 43 + }, 44 + 45 + .web => { 46 + const id = iter.next() orelse return error.InvalidDid; 47 + 48 + const host: []const u8, const port: u16 = blk: { 49 + const end = std.mem.indexOfScalar(u8, id, '%') orelse break :blk .{ id, 443 }; 50 + const port = std.fmt.parseInt(u16, id[end + 3 ..], 10) catch return error.InvalidDid; 51 + break :blk .{ id[0..end], port }; 52 + }; 53 + 54 + const path = iter.rest(); 55 + 56 + return .{ .web = .{ 57 + .host = try gpa.dupe(u8, host), 58 + .port = port, 59 + .path = try gpa.dupe(u8, path), 60 + } }; 61 + }, 62 + } 63 + } 64 + 65 + pub fn deinit(self: Did, gpa: Allocator) void { 66 + switch (self) { 67 + .plc => return, 68 + .web => |web| { 69 + gpa.free(web.host); 70 + gpa.free(web.path); 71 + }, 72 + } 73 + } 74 + 75 + pub fn format( 76 + self: Did, 77 + comptime _: []const u8, 78 + _: std.fmt.FormatOptions, 79 + writer: anytype, 80 + ) !void { 81 + switch (self) { 82 + .plc => |plc| try writer.print("did:plc:{s}", .{&plc}), 83 + .web => |web| try writer.print("did:web:{s}", .{web}), 84 + } 85 + } 86 + }; 87 + 88 + pub const Web = struct { 89 + host: []const u8, 90 + port: u16 = 443, 91 + path: []const u8 = "", 92 + 93 + pub fn format( 94 + self: Web, 95 + comptime _: []const u8, 96 + _: std.fmt.FormatOptions, 97 + writer: anytype, 98 + ) !void { 99 + try writer.print("{s}", .{self.host}); 100 + 101 + switch (self.port) { 102 + 443 => {}, 103 + else => try writer.print("%3A{d}", .{self.port}), 104 + } 105 + 106 + if (self.path.len > 0) { 107 + try writer.print(":{s}", .{self.path}); 108 + } 109 + } 110 + }; 111 + 112 + test "did:plc: valid" { 113 + const id = "aBcDejfkllalsjlkllsjk234"; 114 + const did = try Did.init(undefined, "did:plc:" ++ id); 115 + var out: [24]u8 = undefined; 116 + try std.testing.expectEqualStrings(std.ascii.lowerString(&out, id), &did.plc); 117 + } 118 + 119 + test "did:plc: invalid character" { 120 + const did = "did:plc:0BcDejfkllalsjlkllsjk234"; 121 + try std.testing.expectError(DidError.InvalidDid, Did.init(undefined, did)); 122 + } 123 + 124 + test "did:plc: invalid length" { 125 + const did = "did:plc:kllsjk234"; 126 + try std.testing.expectError(DidError.InvalidDid, Did.init(undefined, did)); 127 + } 128 + 129 + test "did:web: host only" { 130 + const did = "did:web:rockorager.dev"; 131 + const actual = try Did.init(std.testing.allocator, did); 132 + defer actual.deinit(std.testing.allocator); 133 + try std.testing.expectEqualStrings("rockorager.dev", actual.web.host); 134 + try std.testing.expectEqual(443, actual.web.port); 135 + try std.testing.expectEqualStrings("", actual.web.path); 136 + 137 + const fmt = try std.fmt.allocPrint(std.testing.allocator, "{s}", .{actual}); 138 + defer std.testing.allocator.free(fmt); 139 + try std.testing.expectEqualStrings("did:web:rockorager.dev", fmt); 140 + } 141 + 142 + test "did:web: host + port" { 143 + const did = "did:web:rockorager.dev%3a80"; 144 + const actual = try Did.init(std.testing.allocator, did); 145 + defer actual.deinit(std.testing.allocator); 146 + try std.testing.expectEqualStrings("rockorager.dev", actual.web.host); 147 + try std.testing.expectEqual(80, actual.web.port); 148 + try std.testing.expectEqualStrings("", actual.web.path); 149 + 150 + const fmt = try std.fmt.allocPrint(std.testing.allocator, "{s}", .{actual}); 151 + defer std.testing.allocator.free(fmt); 152 + try std.testing.expectEqualStrings("did:web:rockorager.dev%3A80", fmt); 153 + } 154 + 155 + test "did:web: host + path" { 156 + const did = "did:web:rockorager.dev:user:alice"; 157 + const actual = try Did.init(std.testing.allocator, did); 158 + defer actual.deinit(std.testing.allocator); 159 + try std.testing.expectEqualStrings("rockorager.dev", actual.web.host); 160 + try std.testing.expectEqual(443, actual.web.port); 161 + try std.testing.expectEqualStrings("user:alice", actual.web.path); 162 + 163 + const fmt = try std.fmt.allocPrint(std.testing.allocator, "{s}", .{actual}); 164 + defer std.testing.allocator.free(fmt); 165 + try std.testing.expectEqualStrings("did:web:rockorager.dev:user:alice", fmt); 166 + } 167 + 168 + test "did:web: host + port + path" { 169 + const did = "did:web:rockorager.dev%3A80:user:alice"; 170 + const actual = try Did.init(std.testing.allocator, did); 171 + defer actual.deinit(std.testing.allocator); 172 + try std.testing.expectEqualStrings("rockorager.dev", actual.web.host); 173 + try std.testing.expectEqual(80, actual.web.port); 174 + try std.testing.expectEqualStrings("user:alice", actual.web.path); 175 + 176 + const fmt = try std.fmt.allocPrint(std.testing.allocator, "{s}", .{actual}); 177 + defer std.testing.allocator.free(fmt); 178 + try std.testing.expectEqualStrings("did:web:rockorager.dev%3A80:user:alice", fmt); 179 + }
+1 -1
src/jetstream.zig
··· 357 357 const byte2: Byte2 = @bitCast(self.bytes[self.idx + 1]); 358 358 self.idx += 2; 359 359 360 - if (!byte1.final) return error.ContinuationFramesNotSupportedYet; 360 + if (!byte1.final) @panic("TODO: continuuation frame support"); 361 361 if (byte2.mask) return error.MaskNotSupported; 362 362 363 363 const len: usize, const data_start: usize = switch (byte2.len) {
+2
src/root.zig
··· 1 1 const std = @import("std"); 2 2 const testing = std.testing; 3 3 4 + const did = @import("did.zig"); 4 5 const jetstream = @import("jetstream.zig"); 5 6 6 7 pub const Jetstream = jetstream.Stream; 7 8 8 9 test { 10 + _ = @import("did.zig"); 9 11 _ = @import("jetstream.zig"); 10 12 }