parse atproto identifiers #
at:// URIs, DIDs, handles — parsed and validated, no copying.
const std = @import("std");
const zat = @import("zat");
pub fn main() void {
// an at:// URI addresses a record: authority / collection / rkey.
// this one points at a post by jcsalterego.bsky.social.
const at_uri = "at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.feed.post/3mpk2wua4jc2x";
const uri = zat.AtUri.parse(at_uri).?;
std.debug.print("{s:<14}{s}\n", .{ "authority:", uri.authority() });
std.debug.print("{s:<14}{s}\n", .{ "collection:", uri.collection().? });
std.debug.print("{s:<14}{s}\n", .{ "rkey:", uri.rkey().? });
const did = zat.Did.parse("did:plc:vc7f4oafdgxsihk4cry2xpze").?;
std.debug.print("{s:<14}{t}\n", .{ "did method:", did.method() });
// Handle.parse returns null for invalid input
std.debug.print("{s:<14}{s}\n", .{ "bsky.app:", validity("bsky.app") });
std.debug.print("{s:<14}{s}\n", .{ "not a handle:", validity("not a handle") });
}
fn validity(handle: []const u8) []const u8 {
return if (zat.Handle.parse(handle) != null) "valid" else "invalid";
}
authority: did:plc:vc7f4oafdgxsihk4cry2xpze
collection: app.bsky.feed.post
rkey: 3mpk2wua4jc2x
did method: plc
bsky.app: valid
not a handle: invalid
This is examples/01_parse_identifiers.zig verbatim — copy it and zig build example-parse-identifiers.
parse returns ?T (or a struct's own optional) — you get a value only if the input is valid, so the type system stops a malformed identifier from being mistaken for a real one. Same shape for Nsid, Rkey, and Tid.
A parsed identifier points into the string you passed. Nothing is copied, so it's cheap to create and stays valid only while that string is alive. std.Uri.parse works the same way.
The authority in an at:// URI can be a DID or a handle — the same record has a stable DID-based URI and a mutable handle alias for it. And an rkey is chosen by whoever wrote the record: validate its shape, but never read authorization or a naming convention into its value.
See also: the at:// URI, DID, and handle specs; Indigo's syntax is the reference parser. Validating identifiers at the boundary is standard across every PDS and app.
wiring zat into your own build.zig
const zat = b.dependency("zat", .{}).module("zat");
exe.root_module.addImport("zat", zat);
after zig fetch --save https://tangled.org/zat.dev/zat/archive/main.