atproto tools in zig zat.dev
sdk atproto zig

fix: use zig 0.15 apis (ArrayList, http.Client.fetch)

+61 -89
+12 -12
src/internal/did_document.zig
··· 85 85 errdefer allocator.free(id); 86 86 87 87 // parse alsoKnownAs -> handles 88 - var handles = std.ArrayList([]const u8).init(allocator); 88 + var handles: std.ArrayList([]const u8) = .empty; 89 89 errdefer { 90 90 for (handles.items) |h| allocator.free(h); 91 - handles.deinit(); 91 + handles.deinit(allocator); 92 92 } 93 93 94 94 if (obj.get("alsoKnownAs")) |aka| { ··· 101 101 s[5..] 102 102 else 103 103 s; 104 - try handles.append(try allocator.dupe(u8, h)); 104 + try handles.append(allocator, try allocator.dupe(u8, h)); 105 105 } 106 106 } 107 107 } 108 108 } 109 109 110 110 // parse verificationMethod 111 - var vms = std.ArrayList(VerificationMethod).init(allocator); 111 + var vms: std.ArrayList(VerificationMethod) = .empty; 112 112 errdefer { 113 113 for (vms.items) |vm| { 114 114 allocator.free(vm.id); ··· 116 116 allocator.free(vm.controller); 117 117 allocator.free(vm.public_key_multibase); 118 118 } 119 - vms.deinit(); 119 + vms.deinit(allocator); 120 120 } 121 121 122 122 if (obj.get("verificationMethod")) |vm_arr| { ··· 130 130 .controller = try allocator.dupe(u8, getStr(vm_obj, "controller") orelse ""), 131 131 .public_key_multibase = try allocator.dupe(u8, getStr(vm_obj, "publicKeyMultibase") orelse ""), 132 132 }; 133 - try vms.append(vm); 133 + try vms.append(allocator, vm); 134 134 } 135 135 } 136 136 } 137 137 } 138 138 139 139 // parse service 140 - var svcs = std.ArrayList(Service).init(allocator); 140 + var svcs: std.ArrayList(Service) = .empty; 141 141 errdefer { 142 142 for (svcs.items) |svc| { 143 143 allocator.free(svc.id); 144 144 allocator.free(svc.type); 145 145 allocator.free(svc.service_endpoint); 146 146 } 147 - svcs.deinit(); 147 + svcs.deinit(allocator); 148 148 } 149 149 150 150 if (obj.get("service")) |svc_arr| { ··· 157 157 .type = try allocator.dupe(u8, getStr(svc_obj, "type") orelse ""), 158 158 .service_endpoint = try allocator.dupe(u8, getStr(svc_obj, "serviceEndpoint") orelse ""), 159 159 }; 160 - try svcs.append(svc); 160 + try svcs.append(allocator, svc); 161 161 } 162 162 } 163 163 } ··· 166 166 return .{ 167 167 .allocator = allocator, 168 168 .id = id, 169 - .handles = try handles.toOwnedSlice(), 170 - .verification_methods = try vms.toOwnedSlice(), 171 - .services = try svcs.toOwnedSlice(), 169 + .handles = try handles.toOwnedSlice(allocator), 170 + .verification_methods = try vms.toOwnedSlice(allocator), 171 + .services = try svcs.toOwnedSlice(allocator), 172 172 }; 173 173 } 174 174
+17 -31
src/internal/did_resolver.zig
··· 18 18 pub fn init(allocator: std.mem.Allocator) DidResolver { 19 19 return .{ 20 20 .allocator = allocator, 21 - .http_client = std.http.Client{ .allocator = allocator }, 21 + .http_client = .{ .allocator = allocator }, 22 22 }; 23 23 } 24 24 ··· 51 51 const domain_and_path = did.raw["did:web:".len..]; 52 52 53 53 // decode percent-encoded colons in path 54 - var url_buf = std.ArrayList(u8).init(self.allocator); 55 - defer url_buf.deinit(); 54 + var url_buf: std.ArrayList(u8) = .empty; 55 + defer url_buf.deinit(self.allocator); 56 56 57 - try url_buf.appendSlice("https://"); 57 + try url_buf.appendSlice(self.allocator, "https://"); 58 58 59 59 var first_segment = true; 60 60 var it = std.mem.splitScalar(u8, domain_and_path, ':'); 61 61 while (it.next()) |segment| { 62 62 if (first_segment) { 63 63 // first segment is the domain 64 - try url_buf.appendSlice(segment); 64 + try url_buf.appendSlice(self.allocator, segment); 65 65 first_segment = false; 66 66 } else { 67 67 // subsequent segments are path components 68 - try url_buf.append('/'); 69 - try url_buf.appendSlice(segment); 68 + try url_buf.append(self.allocator, '/'); 69 + try url_buf.appendSlice(self.allocator, segment); 70 70 } 71 71 } 72 72 73 73 // add .well-known/did.json or /did.json 74 74 if (std.mem.indexOf(u8, domain_and_path, ":") == null) { 75 75 // no path, use .well-known 76 - try url_buf.appendSlice("/.well-known/did.json"); 76 + try url_buf.appendSlice(self.allocator, "/.well-known/did.json"); 77 77 } else { 78 78 // has path, append did.json 79 - try url_buf.appendSlice("/did.json"); 79 + try url_buf.appendSlice(self.allocator, "/did.json"); 80 80 } 81 81 82 82 return try self.fetchDidDocument(url_buf.items); ··· 84 84 85 85 /// fetch and parse a did document from url 86 86 fn fetchDidDocument(self: *DidResolver, url: []const u8) !DidDocument { 87 - const uri = try std.Uri.parse(url); 88 - 89 - var header_buf: [4096]u8 = undefined; 90 - var req = try self.http_client.open(.GET, uri, .{ 91 - .server_header_buffer = &header_buf, 92 - }); 93 - defer req.deinit(); 87 + var aw: std.Io.Writer.Allocating = .init(self.allocator); 88 + defer aw.deinit(); 94 89 95 - try req.send(); 96 - try req.wait(); 90 + const result = self.http_client.fetch(.{ 91 + .location = .{ .url = url }, 92 + .response_writer = &aw.writer, 93 + }) catch return error.DidResolutionFailed; 97 94 98 - if (req.status != .ok) { 95 + if (result.status != .ok) { 99 96 return error.DidResolutionFailed; 100 97 } 101 98 102 - // read response body 103 - var body = std.ArrayList(u8).init(self.allocator); 104 - defer body.deinit(); 105 - 106 - var buf: [4096]u8 = undefined; 107 - while (true) { 108 - const n = try req.reader().read(&buf); 109 - if (n == 0) break; 110 - try body.appendSlice(buf[0..n]); 111 - } 112 - 113 - return try DidDocument.parse(self.allocator, body.items); 99 + return try DidDocument.parse(self.allocator, aw.toArrayList().items); 114 100 } 115 101 }; 116 102
+32 -46
src/internal/xrpc.zig
··· 21 21 pub fn init(allocator: std.mem.Allocator, host: []const u8) XrpcClient { 22 22 return .{ 23 23 .allocator = allocator, 24 - .http_client = std.http.Client{ .allocator = allocator }, 24 + .http_client = .{ .allocator = allocator }, 25 25 .host = host, 26 26 }; 27 27 } ··· 40 40 const url = try self.buildUrl(nsid, params); 41 41 defer self.allocator.free(url); 42 42 43 - return try self.doRequest(.GET, url, null); 43 + return try self.doRequest(url, null); 44 44 } 45 45 46 46 /// call a procedure method (POST) ··· 48 48 const url = try self.buildUrl(nsid, null); 49 49 defer self.allocator.free(url); 50 50 51 - return try self.doRequest(.POST, url, body); 51 + return try self.doRequest(url, body); 52 52 } 53 53 54 54 fn buildUrl(self: *XrpcClient, nsid: Nsid, params: ?std.StringHashMap([]const u8)) ![]u8 { 55 - var url = std.ArrayList(u8).init(self.allocator); 56 - errdefer url.deinit(); 55 + var url: std.ArrayList(u8) = .empty; 56 + errdefer url.deinit(self.allocator); 57 57 58 - try url.appendSlice(self.host); 59 - try url.appendSlice("/xrpc/"); 60 - try url.appendSlice(nsid.raw); 58 + try url.appendSlice(self.allocator, self.host); 59 + try url.appendSlice(self.allocator, "/xrpc/"); 60 + try url.appendSlice(self.allocator, nsid.raw); 61 61 62 62 if (params) |p| { 63 63 var first = true; 64 64 var it = p.iterator(); 65 65 while (it.next()) |entry| { 66 - try url.append(if (first) '?' else '&'); 66 + try url.append(self.allocator, if (first) '?' else '&'); 67 67 first = false; 68 - try url.appendSlice(entry.key_ptr.*); 69 - try url.append('='); 68 + try url.appendSlice(self.allocator, entry.key_ptr.*); 69 + try url.append(self.allocator, '='); 70 70 // url encode value 71 71 for (entry.value_ptr.*) |c| { 72 72 if (std.ascii.isAlphanumeric(c) or c == '-' or c == '_' or c == '.' or c == '~') { 73 - try url.append(c); 73 + try url.append(self.allocator, c); 74 74 } else { 75 - try url.writer().print("%{X:0>2}", .{c}); 75 + try url.print(self.allocator, "%{X:0>2}", .{c}); 76 76 } 77 77 } 78 78 } 79 79 } 80 80 81 - return try url.toOwnedSlice(); 81 + return try url.toOwnedSlice(self.allocator); 82 82 } 83 83 84 - fn doRequest(self: *XrpcClient, method: std.http.Method, url: []const u8, body: ?[]const u8) !Response { 85 - const uri = try std.Uri.parse(url); 86 - 87 - var header_buf: [8192]u8 = undefined; 88 - var req = try self.http_client.open(method, uri, .{ 89 - .server_header_buffer = &header_buf, 90 - .extra_headers = if (self.access_token) |token| &.{ 91 - .{ .name = "Authorization", .value = try std.fmt.allocPrint(self.allocator, "Bearer {s}", .{token}) }, 92 - } else &.{}, 93 - }); 94 - defer req.deinit(); 95 - 96 - req.transfer_encoding = if (body) |b| .{ .content_length = b.len } else .none; 97 - 98 - try req.send(); 84 + fn doRequest(self: *XrpcClient, url: []const u8, body: ?[]const u8) !Response { 85 + var aw: std.Io.Writer.Allocating = .init(self.allocator); 86 + errdefer aw.deinit(); 99 87 100 - if (body) |b| { 101 - try req.writer().writeAll(b); 102 - try req.finish(); 88 + // build extra headers for auth 89 + var extra_headers: std.http.Client.Request.Headers = .{}; 90 + var auth_header_buf: [256]u8 = undefined; 91 + if (self.access_token) |token| { 92 + const auth_value = try std.fmt.bufPrint(&auth_header_buf, "Bearer {s}", .{token}); 93 + extra_headers.authorization = .{ .override = auth_value }; 103 94 } 104 95 105 - try req.wait(); 106 - 107 - // read response body 108 - var response_body = std.ArrayList(u8).init(self.allocator); 109 - errdefer response_body.deinit(); 110 - 111 - var buf: [4096]u8 = undefined; 112 - while (true) { 113 - const n = try req.reader().read(&buf); 114 - if (n == 0) break; 115 - try response_body.appendSlice(buf[0..n]); 116 - } 96 + const result = self.http_client.fetch(.{ 97 + .location = .{ .url = url }, 98 + .response_writer = &aw.writer, 99 + .method = if (body != null) .POST else .GET, 100 + .payload = body, 101 + .headers = extra_headers, 102 + }) catch return error.RequestFailed; 117 103 118 104 return .{ 119 105 .allocator = self.allocator, 120 - .status = req.status, 121 - .body = try response_body.toOwnedSlice(), 106 + .status = result.status, 107 + .body = try aw.toArrayList().toOwnedSlice(self.allocator), 122 108 }; 123 109 } 124 110