A better Rust ATProto crate

docs fixes

Orual 3cbde135 839ef8ba

Changed files
+19 -30
crates
jacquard
src
identity
jacquard-common
+1 -1
crates/jacquard-common/src/lib.rs
···
pub use smol_str;
pub use url;
-
/// A copy-on-write immutable string type that uses [`SmolStr`] for
/// the "owned" variant.
#[macro_use]
pub mod cowstr;
···
pub use smol_str;
pub use url;
+
/// A copy-on-write immutable string type that uses [`smol_str::SmolStr`] for
/// the "owned" variant.
#[macro_use]
pub mod cowstr;
-2
crates/jacquard-common/src/types/crypto.rs
···
/// Known multicodec key codecs for Multikey public keys
///
-
-
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyCodec {
/// Ed25519
···
/// Known multicodec key codecs for Multikey public keys
///
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyCodec {
/// Ed25519
+2 -2
crates/jacquard-common/src/types/did_doc.rs
···
#[serde(borrow)]
pub id: Did<'a>,
-
/// Alternate identifiers for the subject, such as at://<handle>
#[serde(borrow)]
pub also_known_as: Option<Vec<CowStr<'a>>>,
···
}
impl<'a> DidDocument<'a> {
-
/// Extract validated handles from `alsoKnownAs` entries like `at://<handle>`.
pub fn handles(&self) -> Vec<Handle<'static>> {
self.also_known_as
.as_ref()
···
#[serde(borrow)]
pub id: Did<'a>,
+
/// Alternate identifiers for the subject, such as at://\<handle\>
#[serde(borrow)]
pub also_known_as: Option<Vec<CowStr<'a>>>,
···
}
impl<'a> DidDocument<'a> {
+
/// Extract validated handles from `alsoKnownAs` entries like `at://\<handle\>`.
pub fn handles(&self) -> Vec<Handle<'static>> {
self.also_known_as
.as_ref()
+2 -2
crates/jacquard/src/identity/resolver.rs
···
/// Handle → DID fallback step.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HandleStep {
-
/// DNS TXT _atproto.<handle>
DnsTxt,
-
/// HTTPS GET https://<handle>/.well-known/atproto-did
HttpsWellKnown,
/// XRPC com.atproto.identity.resolveHandle against a provided PDS base
PdsResolveHandle,
···
/// Handle → DID fallback step.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HandleStep {
+
/// DNS TXT _atproto.\<handle\>
DnsTxt,
+
/// HTTPS GET https://\<handle\>/.well-known/atproto-did
HttpsWellKnown,
/// XRPC com.atproto.identity.resolveHandle against a provided PDS base
PdsResolveHandle,
+14 -23
crates/jacquard/src/lib.rs
···
//! #[tokio::main]
//! async fn main() -> miette::Result<()> {
//! let args = Args::parse();
-
//!
//! // Create HTTP client
//! let url = url::Url::parse(&args.pds).unwrap();
//! let client = BasicClient::new(url);
-
//!
//! // Create session
//! let session = Session::from(
//! client
···
//! .await?
//! .into_output()?,
//! );
-
//!
-
//! println!("logged in as {} ({})", session.handle, session.did);
//! client.set_session(session).await.unwrap();
-
//!
//! // Fetch timeline
//! println!("\nfetching timeline...");
//! let timeline = client
//! .send(GetTimeline::new().limit(5).build())
//! .await?
//! .into_output()?;
-
//!
//! println!("\ntimeline ({} posts):", timeline.feed.len());
//! for (i, post) in timeline.feed.iter().enumerate() {
//! println!("\n{}. by {}", i + 1, post.post.author.handle);
···
//! serde_json::to_string_pretty(&post.post.record).into_diagnostic()?
//! );
//! }
-
//!
//! Ok(())
//! }
//! ```
//!
-
//! ## Clients
//!
//! - Stateless XRPC: any `HttpClient` (e.g., `reqwest::Client`) implements `XrpcExt`,
//! which provides `xrpc(base: Url) -> XrpcCall` for per-request calls with
//! optional `CallOptions` (auth, proxy, labelers, headers). Useful when you
//! want to pass auth on each call or build advanced flows.
-
//! Example
-
//! ```ignore
-
//! use jacquard::client::XrpcExt;
-
//! use jacquard::api::app_bsky::feed::get_author_feed::GetAuthorFeed;
-
//! use jacquard::types::ident::AtIdentifier;
-
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let http = reqwest::Client::new();
···
//! `AtClient<reqwest::Client, MemoryTokenStore>` with a `new(Url)` constructor.
//!
//! Per-request overrides (stateless)
-
//! ```ignore
-
//! use jacquard::client::{XrpcExt, AuthorizationToken};
-
//! use jacquard::api::app_bsky::feed::get_author_feed::GetAuthorFeed;
-
//! use jacquard::types::ident::AtIdentifier;
-
//! use jacquard::CowStr;
-
//! use miette::IntoDiagnostic;
-
//!
//! #[tokio::main]
//! async fn main() -> miette::Result<()> {
//! let http = reqwest::Client::new();
···
//! - Use `MemoryTokenStore` for ephemeral sessions, tests, and CLIs.
//! - For persistence, `FileTokenStore` stores session tokens as JSON on disk.
//! See `client::token::FileTokenStore` docs for details.
-
//! Example
-
//! ```ignore
//! use jacquard::client::{AtClient, FileTokenStore};
//! let base = url::Url::parse("https://bsky.social").unwrap();
//! let store = FileTokenStore::new("/tmp/jacquard-session.json");
···
//! #[tokio::main]
//! async fn main() -> miette::Result<()> {
//! let args = Args::parse();
//! // Create HTTP client
//! let url = url::Url::parse(&args.pds).unwrap();
//! let client = BasicClient::new(url);
//! // Create session
//! let session = Session::from(
//! client
···
//! .await?
//! .into_output()?,
//! );
//! client.set_session(session).await.unwrap();
//! // Fetch timeline
//! println!("\nfetching timeline...");
//! let timeline = client
//! .send(GetTimeline::new().limit(5).build())
//! .await?
//! .into_output()?;
//! println!("\ntimeline ({} posts):", timeline.feed.len());
//! for (i, post) in timeline.feed.iter().enumerate() {
//! println!("\n{}. by {}", i + 1, post.post.author.handle);
···
//! serde_json::to_string_pretty(&post.post.record).into_diagnostic()?
//! );
//! }
//! Ok(())
//! }
//! ```
//!
+
//! ## Client options:
//!
//! - Stateless XRPC: any `HttpClient` (e.g., `reqwest::Client`) implements `XrpcExt`,
//! which provides `xrpc(base: Url) -> XrpcCall` for per-request calls with
//! optional `CallOptions` (auth, proxy, labelers, headers). Useful when you
//! want to pass auth on each call or build advanced flows.
+
//! ```no_run
+
//! # use jacquard::client::XrpcExt;
+
//! # use jacquard::api::app_bsky::feed::get_author_feed::GetAuthorFeed;
+
//! # use jacquard::types::ident::AtIdentifier;
+
//! #
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let http = reqwest::Client::new();
···
//! `AtClient<reqwest::Client, MemoryTokenStore>` with a `new(Url)` constructor.
//!
//! Per-request overrides (stateless)
+
//! ```no_run
+
//! # use jacquard::client::{XrpcExt, AuthorizationToken};
+
//! # use jacquard::api::app_bsky::feed::get_author_feed::GetAuthorFeed;
+
//! # use jacquard::types::ident::AtIdentifier;
+
//! # use jacquard::CowStr;
+
//! # use miette::IntoDiagnostic;
+
//! #
//! #[tokio::main]
//! async fn main() -> miette::Result<()> {
//! let http = reqwest::Client::new();
···
//! - Use `MemoryTokenStore` for ephemeral sessions, tests, and CLIs.
//! - For persistence, `FileTokenStore` stores session tokens as JSON on disk.
//! See `client::token::FileTokenStore` docs for details.
+
//! ```no_run
//! use jacquard::client::{AtClient, FileTokenStore};
//! let base = url::Url::parse("https://bsky.social").unwrap();
//! let store = FileTokenStore::new("/tmp/jacquard-session.json");