1# Jacquard 2 3A suite of Rust crates for the AT Protocol. 4 5[![Crates.io](https://img.shields.io/crates/v/jacquard.svg)](https://crates.io/crates/jacquard) [![Documentation](https://docs.rs/jacquard/badge.svg)](https://docs.rs/jacquard) [![License](https://img.shields.io/crates/l/jacquard.svg)](./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.