CLAUDE.md#
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview#
Jacquard is a suite of Rust crates for the AT Protocol (atproto/Bluesky). The project emphasizes spec-compliant, validated, performant baseline types with minimal boilerplate. Key design goals:
- Validated AT Protocol types including typed at:// URIs
- Custom lexicon extension support
- Lexicon
Valuetype for working with unknown atproto data (dag-cbor or json) - Using as much or as little of the crates as needed
Workspace Structure#
This is a Cargo workspace with several crates:
- jacquard: Main library crate with XRPC client and public API surface (re-exports jacquard-api and jacquard-common)
- jacquard-common: Core AT Protocol types (DIDs, handles, at-URIs, NSIDs, TIDs, CIDs, etc.) and the
CowStrtype for efficient string handling - jacquard-lexicon: Lexicon parsing and Rust code generation from lexicon schemas
- jacquard-api: Generated API bindings from lexicon schemas (implementation detail, not directly used by consumers)
- jacquard-derive: Attribute macros (
#[lexicon],#[open_union]) for lexicon structures
Development Commands#
Using Nix (preferred)#
# Enter dev shell
nix develop
# Build
nix build
# Run
nix develop -c cargo run
Using Cargo/Just#
# Build
cargo build
# Run tests
cargo test
# Run specific test
cargo test <test_name>
# Run specific package tests
cargo test -p <package_name>
# Run
cargo run
# Auto-recompile and run
just watch [ARGS]
# Format and lint all
just pre-commit-all
# Generate API bindings from lexicon schemas
cargo run -p jacquard-lexicon --bin jacquard-codegen -- -i <input_dir> -o <output_dir> [-r <root_module>]
# Example:
cargo run -p jacquard-lexicon --bin jacquard-codegen -- -i crates/jacquard-lexicon/tests/fixtures/lexicons/atproto/lexicons -o crates/jacquard-api/src -r crate
String Type Pattern#
The codebase uses a consistent pattern for validated string types. Each type should have:
Constructors#
new(): Construct from a string slice with appropriate lifetime (borrows)new_owned(): Construct fromimpl AsRef<str>, taking ownershipnew_static(): Construct from&'static strusingSmolStr/CowStr's static constructor (no allocation)raw(): Same asnew()but panics instead of returningResultunchecked(): Same asnew()but doesn't validate (markedunsafe)as_str(): Return string slice
Traits#
All string types should implement:
Serialize+Deserialize(custom impl for latter, sometimes for former)FromStr,DisplayDebug,PartialEq,Eq,Hash,CloneFrom<T> for String,CowStr,SmolStrFrom<String>,From<CowStr>,From<SmolStr>, orTryFromif likely to failAsRef<str>DerefwithTarget = str(usually)
Implementation Details#
- Use
#[repr(transparent)]when possible (exception: at-uri type and components) - Use
SmolStrdirectly as inner type if most instances will be under 24 bytes - Use
CowStrfor longer strings to allow borrowing from input - Implement
IntoStatictrait to take ownership of string types
Code Style#
- Avoid comments for self-documenting code
- Comments should not detail fixes when refactoring
- Professional writing within source code and comments only
- Prioritize long-term maintainability over implementation speed
Testing#
- Write test cases for all critical code
- Tests can be run per-package or workspace-wide
- Use
cargo test <name>to run specific tests - Current test coverage: 89 tests in jacquard-common
Lexicon Code Generation#
The jacquard-codegen binary generates Rust types from AT Protocol Lexicon schemas:
- Generates structs with
#[lexicon]attribute for forward compatibility (captures unknown fields inextra_data) - Generates enums with
#[open_union]attribute for handling unknown variants (unless markedclosedin lexicon) - Resolves local refs (e.g.,
#imagebecomesImage<'a>) - Extracts doc comments from lexicon
descriptionfields - Adds header comments with
@generatedmarker and lexicon NSID - Handles XRPC queries, procedures, subscriptions, and errors
- Generates proper module tree with Rust 2018 style
- XrpcRequest trait: Implemented directly on params/input structs (not marker types), with GATs for Output<'de> and Err<'de>
- IntoStatic trait: All generated types implement
IntoStaticto convert borrowed types to owned ('static) variants - Collection trait: Implemented on record types directly, with const NSID
Current State & Next Steps#
Completed#
- ✅ Comprehensive validation tests for all core string types (handle, DID, NSID, TID, record key, AT-URI, datetime, language, identifier)
- ✅ Validated implementations against AT Protocol specs and TypeScript reference implementation
- ✅ String type interface standardization (Language now has
new_static(), Datetime has full conversion traits) - ✅ Data serialization: Full serialize/deserialize for
Data<'_>,Array,Objectwith format-specific handling (JSON vs CBOR) - ✅ CidLink wrapper type with automatic
{"$link": "cid"}serialization in JSON - ✅ Integration test with real Bluesky thread data validates round-trip correctness
- ✅ Lexicon code generation with forward compatibility and proper lifetime handling
- ✅ IntoStatic implementations for all generated types (structs, enums, unions)
- ✅ XrpcRequest trait with GATs, implemented on params/input types directly
- ✅ HttpClient and XrpcClient traits with generic send_xrpc implementation
- ✅ Response wrapper with parse() (borrowed) and into_output() (owned) methods
- ✅ Structured error types (ClientError, TransportError, EncodeError, DecodeError, HttpError, AuthError)
Next Steps#
- Concrete HttpClient Implementation: Implement HttpClient for reqwest::Client and potentially other HTTP clients
- Error Handling Improvements: Add XRPC error parsing, better HTTP status code handling, structured error responses
- Authentication: Session management, token refresh, DPoP support
- Body Encoding: Support for non-JSON encodings (CBOR, multipart, etc.) in procedures
- Lexicon Resolution: Fetch lexicons from web sources (atproto authorities, git repositories) and parse into corpus
- Custom Lexicon Support: Allow users to plug in their own generated lexicons alongside jacquard-api types in the client/server layer
- Public API: Design the main API surface in
jacquardthat re-exports and wraps generated types - DID Document Support: Parsing, validation, and resolution of DID documents
- OAuth Implementation: OAuth flow support for authentication
- Examples & Documentation: Create examples and improve documentation
- Testing: Comprehensive tests for generated code and round-trip serialization