1# Jacquard
2
3A suite of Rust crates for the AT Protocol.
4
5[](https://crates.io/crates/jacquard) [](https://docs.rs/jacquard) [](./LICENSE)
6
7## Goals
8
9- Validated, spec-compliant, easy to work with, and performant baseline types (including typed at:// uris)
10- Batteries-included, but easily replaceable batteries.
11 - Easy to extend with custom lexicons
12- lexicon Value type for working with unknown atproto data (dag-cbor or json)
13- order of magnitude less boilerplate than some existing crates
14 - either the codegen produces code that's easy to work with, or there are good handwritten wrappers
15- didDoc type with helper methods for getting handles, multikey, and PDS endpoint
16- use as much or as little from the crates as you need
17
18
19## Example
20
21Dead simple api client. Logs in, prints the latest 5 posts from your timeline.
22
23```rust
24use clap::Parser;
25use jacquard::CowStr;
26use jacquard::api::app_bsky::feed::get_timeline::GetTimeline;
27use jacquard::api::com_atproto::server::create_session::CreateSession;
28use jacquard::client::{AuthenticatedClient, Session, XrpcClient};
29use miette::IntoDiagnostic;
30
31#[derive(Parser, Debug)]
32#[command(author, version, about = "Jacquard - AT Protocol client demo")]
33struct Args {
34 /// Username/handle (e.g., alice.mosphere.at)
35 #[arg(short, long)]
36 username: CowStr<'static>,
37
38 /// PDS URL (e.g., https://bsky.social)
39 #[arg(long, default_value = "https://bsky.social")]
40 pds: CowStr<'static>,
41
42 /// App password
43 #[arg(short, long)]
44 password: CowStr<'static>,
45}
46
47#[tokio::main]
48async fn main() -> miette::Result<()> {
49 let args = Args::parse();
50
51 // Create HTTP client
52 let mut client = AuthenticatedClient::new(reqwest::Client::new(), args.pds);
53
54 // Create session
55 let session = Session::from(
56 client
57 .send(
58 CreateSession::new()
59 .identifier(args.username)
60 .password(args.password)
61 .build(),
62 )
63 .await?
64 .into_output()?,
65 );
66
67 println!("logged in as {} ({})", session.handle, session.did);
68 client.set_session(session);
69
70 // Fetch timeline
71 println!("\nfetching timeline...");
72 let timeline = client
73 .send(GetTimeline::new().limit(5).build())
74 .await?
75 .into_output()?;
76
77 println!("\ntimeline ({} posts):", timeline.feed.len());
78 for (i, post) in timeline.feed.iter().enumerate() {
79 println!("\n{}. by {}", i + 1, post.post.author.handle);
80 println!(
81 " {}",
82 serde_json::to_string_pretty(&post.post.record).into_diagnostic()?
83 );
84 }
85
86 Ok(())
87}
88```
89
90## Development
91
92This repo uses [Flakes](https://nixos.asia/en/flakes) from the get-go.
93
94```bash
95# Dev shell
96nix develop
97
98# or run via cargo
99nix develop -c cargo run
100
101# build
102nix build
103```
104
105There's also a [`justfile`](https://just.systems/) for Makefile-esque commands to be run inside of the devShell, and you can generally `cargo ...` or `just ...` whatever just fine if you don't want to use Nix and have the prerequisites installed.