A better Rust ATProto crate
1# CLAUDE.md
2
3This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
5## Project Overview
6
7Jacquard 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:
8
9- Validated AT Protocol types including typed at:// URIs
10- Custom lexicon extension support
11- Lexicon `Value` type for working with unknown atproto data (dag-cbor or json)
12- Using as much or as little of the crates as needed
13
14## Workspace Structure
15
16This is a Cargo workspace with several crates:
17
18- **jacquard**: Main library crate with XRPC client and public API surface (re-exports jacquard-api and jacquard-common)
19- **jacquard-common**: Core AT Protocol types (DIDs, handles, at-URIs, NSIDs, TIDs, CIDs, etc.) and the `CowStr` type for efficient string handling
20- **jacquard-lexicon**: Lexicon parsing and Rust code generation from lexicon schemas
21- **jacquard-api**: Generated API bindings from lexicon schemas (implementation detail, not directly used by consumers)
22- **jacquard-derive**: Attribute macros (`#[lexicon]`, `#[open_union]`) for lexicon structures
23
24## Development Commands
25
26### Using Nix (preferred)
27```bash
28# Enter dev shell
29nix develop
30
31# Build
32nix build
33
34# Run
35nix develop -c cargo run
36```
37
38### Using Cargo/Just
39```bash
40# Build
41cargo build
42
43# Run tests
44cargo test
45
46# Run specific test
47cargo test <test_name>
48
49# Run specific package tests
50cargo test -p <package_name>
51
52# Run
53cargo run
54
55# Auto-recompile and run
56just watch [ARGS]
57
58# Format and lint all
59just pre-commit-all
60
61# Generate API bindings from lexicon schemas
62cargo run -p jacquard-lexicon --bin jacquard-codegen -- -i <input_dir> -o <output_dir> [-r <root_module>]
63# Example:
64cargo run -p jacquard-lexicon --bin jacquard-codegen -- -i crates/jacquard-lexicon/tests/fixtures/lexicons/atproto/lexicons -o crates/jacquard-api/src -r crate
65```
66
67## String Type Pattern
68
69The codebase uses a consistent pattern for validated string types. Each type should have:
70
71### Constructors
72- `new()`: Construct from a string slice with appropriate lifetime (borrows)
73- `new_owned()`: Construct from `impl AsRef<str>`, taking ownership
74- `new_static()`: Construct from `&'static str` using `SmolStr`/`CowStr`'s static constructor (no allocation)
75- `raw()`: Same as `new()` but panics instead of returning `Result`
76- `unchecked()`: Same as `new()` but doesn't validate (marked `unsafe`)
77- `as_str()`: Return string slice
78
79### Traits
80All string types should implement:
81- `Serialize` + `Deserialize` (custom impl for latter, sometimes for former)
82- `FromStr`, `Display`
83- `Debug`, `PartialEq`, `Eq`, `Hash`, `Clone`
84- `From<T> for String`, `CowStr`, `SmolStr`
85- `From<String>`, `From<CowStr>`, `From<SmolStr>`, or `TryFrom` if likely to fail
86- `AsRef<str>`
87- `Deref` with `Target = str` (usually)
88
89### Implementation Details
90- Use `#[repr(transparent)]` when possible (exception: at-uri type and components)
91- Use `SmolStr` directly as inner type if most instances will be under 24 bytes
92- Use `CowStr` for longer strings to allow borrowing from input
93- Implement `IntoStatic` trait to take ownership of string types
94
95## Code Style
96
97- Avoid comments for self-documenting code
98- Comments should not detail fixes when refactoring
99- Professional writing within source code and comments only
100- Prioritize long-term maintainability over implementation speed
101
102## Testing
103
104- Write test cases for all critical code
105- Tests can be run per-package or workspace-wide
106- Use `cargo test <name>` to run specific tests
107- Current test coverage: 89 tests in jacquard-common
108
109## Lexicon Code Generation
110
111The `jacquard-codegen` binary generates Rust types from AT Protocol Lexicon schemas:
112
113- Generates structs with `#[lexicon]` attribute for forward compatibility (captures unknown fields in `extra_data`)
114- Generates enums with `#[open_union]` attribute for handling unknown variants (unless marked `closed` in lexicon)
115- Resolves local refs (e.g., `#image` becomes `Image<'a>`)
116- Extracts doc comments from lexicon `description` fields
117- Adds header comments with `@generated` marker and lexicon NSID
118- Handles XRPC queries, procedures, subscriptions, and errors
119- Generates proper module tree with Rust 2018 style
120- **XrpcRequest trait**: Implemented directly on params/input structs (not marker types), with GATs for Output<'de> and Err<'de>
121- **IntoStatic trait**: All generated types implement `IntoStatic` to convert borrowed types to owned ('static) variants
122- **Collection trait**: Implemented on record types directly, with const NSID
123
124## Current State & Next Steps
125
126### Completed
127- ✅ Comprehensive validation tests for all core string types (handle, DID, NSID, TID, record key, AT-URI, datetime, language, identifier)
128- ✅ Validated implementations against AT Protocol specs and TypeScript reference implementation
129- ✅ String type interface standardization (Language now has `new_static()`, Datetime has full conversion traits)
130- ✅ Data serialization: Full serialize/deserialize for `Data<'_>`, `Array`, `Object` with format-specific handling (JSON vs CBOR)
131- ✅ CidLink wrapper type with automatic `{"$link": "cid"}` serialization in JSON
132- ✅ Integration test with real Bluesky thread data validates round-trip correctness
133- ✅ Lexicon code generation with forward compatibility and proper lifetime handling
134- ✅ IntoStatic implementations for all generated types (structs, enums, unions)
135- ✅ XrpcRequest trait with GATs, implemented on params/input types directly
136- ✅ HttpClient and XrpcClient traits with generic send_xrpc implementation
137- ✅ Response wrapper with parse() (borrowed) and into_output() (owned) methods
138- ✅ Structured error types (ClientError, TransportError, EncodeError, DecodeError, HttpError, AuthError)
139
140### Next Steps
1411. **Concrete HttpClient Implementation**: Implement HttpClient for reqwest::Client and potentially other HTTP clients
1422. **Error Handling Improvements**: Add XRPC error parsing, better HTTP status code handling, structured error responses
1433. **Authentication**: Session management, token refresh, DPoP support
1444. **Body Encoding**: Support for non-JSON encodings (CBOR, multipart, etc.) in procedures
1455. **Lexicon Resolution**: Fetch lexicons from web sources (atproto authorities, git repositories) and parse into corpus
1466. **Custom Lexicon Support**: Allow users to plug in their own generated lexicons alongside jacquard-api types in the client/server layer
1477. **Public API**: Design the main API surface in `jacquard` that re-exports and wraps generated types
1488. **DID Document Support**: Parsing, validation, and resolution of DID documents
1499. **OAuth Implementation**: OAuth flow support for authentication
15010. **Examples & Documentation**: Create examples and improve documentation
15111. **Testing**: Comprehensive tests for generated code and round-trip serialization