Crates.io | atproto-lexicon |
lib.rs | atproto-lexicon |
version | 0.13.0 |
created_at | 2025-09-22 01:16:08.429664+00 |
updated_at | 2025-09-22 01:16:08.429664+00 |
description | AT Protocol lexicon resolution and validation |
homepage | https://tangled.sh/@smokesignal.events/atproto-identity-rs |
repository | https://tangled.sh/@smokesignal.events/atproto-identity-rs |
max_upload_size | |
id | 1849411 |
size | 155,452 |
AT Protocol lexicon resolution and validation library for Rust.
This library provides functionality for resolving and validating AT Protocol lexicons, which define the schema and structure of AT Protocol data. It implements the full lexicon resolution chain as specified by the AT Protocol:
_lexicon
prefixAdd this to your Cargo.toml
:
[dependencies]
atproto-lexicon = "0.13.0"
use atproto_lexicon::resolve::{DefaultLexiconResolver, LexiconResolver};
use atproto_identity::resolve::HickoryDnsResolver;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let http_client = reqwest::Client::new();
let dns_resolver = HickoryDnsResolver::create_resolver(&[]);
let resolver = DefaultLexiconResolver::new(http_client, dns_resolver);
// Resolve a single lexicon
let lexicon = resolver.resolve("app.bsky.feed.post").await?;
println!("Resolved lexicon: {}", serde_json::to_string_pretty(&lexicon)?);
Ok(())
}
use atproto_lexicon::resolve_recursive::{RecursiveLexiconResolver, RecursiveResolverConfig};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let http_client = reqwest::Client::new();
let dns_resolver = HickoryDnsResolver::create_resolver(&[]);
let resolver = DefaultLexiconResolver::new(http_client, dns_resolver);
let config = RecursiveResolverConfig {
max_depth: 5, // Maximum recursion depth
include_entry: true, // Include the entry lexicon in results
};
let recursive_resolver = RecursiveLexiconResolver::with_config(resolver, config);
// Resolve a lexicon and all its dependencies
let lexicons = recursive_resolver.resolve_recursive("app.bsky.feed.post").await?;
for (nsid, schema) in lexicons {
println!("Resolved {}: {} bytes", nsid,
serde_json::to_string(&schema)?.len());
}
Ok(())
}
use atproto_lexicon::validation::{
is_valid_nsid, parse_nsid, nsid_to_dns_name, absolute
};
// Validate NSIDs
assert!(is_valid_nsid("app.bsky.feed.post"));
assert!(!is_valid_nsid("invalid"));
// Parse NSID components
let parts = parse_nsid("app.bsky.feed.post#reply", None)?;
assert_eq!(parts.parts, vec!["app", "bsky", "feed", "post"]);
assert_eq!(parts.fragment, Some("reply".to_string()));
// Convert parsed NSID back to string using Display trait
println!("Parsed NSID: {}", parts); // Outputs: app.bsky.feed.post#reply
// Convert NSID to DNS name for resolution
let dns_name = nsid_to_dns_name("app.bsky.feed.post")?;
assert_eq!(dns_name, "_lexicon.feed.bsky.app");
// Make fragment-only references absolute
assert_eq!(absolute("app.bsky.feed.post", "#reply"), "app.bsky.feed.post#reply");
assert_eq!(absolute("app.bsky.feed.post", "com.example.other"), "com.example.other");
use atproto_lexicon::resolve_recursive::extract_lexicon_references;
use serde_json::json;
let schema = json!({
"lexicon": 1,
"id": "app.bsky.feed.post",
"defs": {
"main": {
"type": "record",
"record": {
"type": "object",
"properties": {
"embed": {
"type": "union",
"refs": [
{ "type": "ref", "ref": "app.bsky.embed.images" },
{ "type": "ref", "ref": "#localref" } // Fragment reference
]
}
}
}
}
}
});
let references = extract_lexicon_references(&schema);
// References will include:
// - "app.bsky.embed.images" (external reference)
// - "app.bsky.feed.post" (from #localref using the lexicon's id as context)
The crate includes a command-line tool for lexicon resolution:
# Build with CLI support
cargo build --features clap --bin atproto-lexicon-resolve
# Resolve a single lexicon
cargo run --features clap --bin atproto-lexicon-resolve -- app.bsky.feed.post
# Pretty print the output
cargo run --features clap --bin atproto-lexicon-resolve -- --pretty app.bsky.feed.post
# Recursively resolve all referenced lexicons
cargo run --features clap --bin atproto-lexicon-resolve -- --recursive app.bsky.feed.post
# Limit recursion depth
cargo run --features clap --bin atproto-lexicon-resolve -- --recursive --max-depth 3 app.bsky.feed.post
# Show dependency graph
cargo run --features clap --bin atproto-lexicon-resolve -- --recursive --show-deps app.bsky.feed.post
# List only direct references
cargo run --features clap --bin atproto-lexicon-resolve -- --list-refs app.bsky.feed.post
errors
: Structured error types for all lexicon operationsresolve
: Core lexicon resolution implementation following AT Protocol specificationresolve_recursive
: Recursive resolution with dependency tracking and cycle detectionvalidation
: NSID validation, parsing, and helper functionsNsidParts
Represents a parsed NSID with its component parts and optional fragment:
parts
: Vector of NSID components (e.g., ["app", "bsky", "feed", "post"]
)fragment
: Optional fragment identifier (e.g., "reply"
for #reply
)Implements Display
trait for converting back to string format.
RecursiveResolverConfig
Configuration for recursive resolution:
max_depth
: Maximum recursion depth (default: 10)include_entry
: Whether to include the entry lexicon in results (default: true)RecursiveResolutionResult
Detailed results from recursive resolution:
lexicons
: HashMap of resolved lexicons by NSIDfailed
: Set of NSIDs that couldn't be resolveddependencies
: Dependency graph showing which lexicons reference which#localref
) using the lexicon's id
field as contextref
objects and union
types with refs
arraysThe library uses structured error types following the project convention error-atproto-lexicon-<domain>-<number>
:
LexiconResolveError
: Resolution errors (no DIDs found, invalid DID format, PDS errors)LexiconValidationError
: NSID format and validation errorsLexiconSchemaError
: Schema structure and parsing errorsLexiconRecursiveError
: Errors specific to recursive resolutionAll errors implement the Error
trait and provide detailed context about failures.
atproto-identity
: For DID resolution and DNS operationsatproto-client
: For XRPC communicationserde_json
: For JSON schema handlingasync-trait
: For async trait definitionstracing
: For structured loggingThis project is part of the atproto-identity-rs workspace. See the root LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.