···11-use serde::{Deserialize, Deserializer, Serialize};
11+use serde::{Deserialize, Serialize};
22use smol_str::SmolStr;
33use std::{
44 borrow::Cow,
···991010use crate::IntoStatic;
11111212-/// Shamelessly copied from [](https://github.com/bearcove/merde)
1312/// A copy-on-write immutable string type that uses [`SmolStr`] for
1413/// the "owned" variant.
1514///
1615/// The standard [`Cow`] type cannot be used, since
1716/// `<str as ToOwned>::Owned` is `String`, and not `SmolStr`.
1717+///
1818+/// Shamelessly ported from [merde](https://github.com/bearcove/merde)
1819#[derive(Clone)]
1920pub enum CowStr<'s> {
2021 /// &str varaiant
···330331 {
331332 deserializer.deserialize_str(CowStrVisitor)
332333 }
333333-}
334334-335335-/// Serde helper for deserializing stuff when you want an owned version
336336-pub fn deserialize_owned<'de, T, D>(deserializer: D) -> Result<<T as IntoStatic>::Output, D::Error>
337337-where
338338- T: Deserialize<'de> + IntoStatic,
339339- D: Deserializer<'de>,
340340-{
341341- let value = T::deserialize(deserializer)?;
342342- Ok(value.into_static())
343334}
344335345336/// Convert to a CowStr.
+2-1
crates/jacquard-common/src/into_static.rs
···77use std::hash::Hash;
88use std::sync::Arc;
991010-/// Shamelessly copied from [](https://github.com/bearcove/merde)
1110/// Allow turning a value into an "owned" variant, which can then be
1211/// returned, moved, etc.
1312///
1413/// This usually involves allocating buffers for `Cow<'a, str>`, etc.
1414+///
1515+/// Shamelessly copied from [merde](https://github.com/bearcove/merde)
1516pub trait IntoStatic: Sized {
1617 /// The "owned" variant of the type. For `Cow<'a, str>`, this is `Cow<'static, str>`, for example.
1718 type Output: 'static;
+12-2
crates/jacquard-common/src/lib.rs
···211211/// HTTP client abstraction used by jacquard crates.
212212pub mod http_client;
213213pub mod macros;
214214-/// Generic session storage traits and utilities.
215215-pub mod session;
216214/// Service authentication JWT parsing and verification.
217215#[cfg(feature = "service-auth")]
218216pub mod service_auth;
217217+/// Generic session storage traits and utilities.
218218+pub mod session;
219219/// Baseline fundamental AT Protocol data types.
220220pub mod types;
221221// XRPC protocol types and traits
···239239 }
240240 }
241241}
242242+243243+/// Serde helper for deserializing stuff when you want an owned version
244244+pub fn deserialize_owned<'de, T, D>(deserializer: D) -> Result<<T as IntoStatic>::Output, D::Error>
245245+where
246246+ T: serde::Deserialize<'de> + IntoStatic,
247247+ D: serde::Deserializer<'de>,
248248+{
249249+ let value = T::deserialize(deserializer)?;
250250+ Ok(value.into_static())
251251+}
+1-1
crates/jacquard-common/src/types/collection.rs
···2121 const NSID: &'static str;
22222323 /// A marker type implementing [`XrpcResp`] that allows typed deserialization of records
2424- /// from this collection. Used by [`Agent::get_record`] to return properly typed responses.
2424+ /// from this collection. Used by [`AgentSessionExt::get_record`](https://docs.rs/jacquard/latest/jacquard/client/trait.AgentSessionExt.html) to return properly typed responses.
2525 type Record: XrpcResp;
26262727 /// Returns the [`Nsid`] for the Lexicon that defines the schema of records in this
+79-25
crates/jacquard/src/lib.rs
···11//! # Jacquard
22//!
33-//! A suite of Rust crates for the AT Protocol.
33+//! A suite of Rust crates intended to make it much easier to get started with atproto development,
44+//! without sacrificing flexibility or performance.
55+//!
66+//! [Jacquard is simpler](https://whtwnd.com/nonbinary.computer/3m33efvsylz2s) because it is
77+//! designed in a way which makes things simple that almost every other atproto library seems to make difficult.
88+//!
99+//! It is also designed around zero-copy/borrowed deserialization: types like [`Post<'_>`](https://docs.rs/jacquard-api/latest/jacquard_api/app_bsky/feed/post/struct.Post.html) can borrow data (via the [`CowStr<'_>`](https://docs.rs/jacquard/latest/jacquard/cowstr/enum.CowStr.html) type and a host of other types built on top of it) directly from the response buffer instead of allocating owned copies. Owned versions are themselves mostly inlined or reference-counted pointers and are therefore still quite efficient. The `IntoStatic` trait (which is derivable) makes it easy to get an owned version and avoid worrying about lifetimes.
410//!
511//!
612//! ## Goals and Features
713//!
814//! - Validated, spec-compliant, easy to work with, and performant baseline types
915//! - Batteries-included, but easily replaceable batteries.
1010-//! - Easy to extend with custom lexicons
1111-//! - Straightforward OAuth
1212-//! - stateless options (or options where you handle the state) for rolling your own
1313-//! - all the building blocks of the convenient abstractions are available
1414-//! - lexicon Value type for working with unknown atproto data (dag-cbor or json)
1515-//! - order of magnitude less boilerplate than some existing crates
1616-//! - use as much or as little from the crates as you need
1616+//! - Easy to extend with custom lexicons using code generation or handwritten api types
1717+//! - Straightforward OAuth
1818+//! - Stateless options (or options where you handle the state) for rolling your own
1919+//! - All the building blocks of the convenient abstractions are available
2020+//! - Server-side convenience features
2121+//! - Lexicon Data value type for working with unknown atproto data (dag-cbor or json)
2222+//! - An order of magnitude less boilerplate than some existing crates
2323+//! - Use as much or as little from the crates as you need
2424+//!
1725//!
1826//!
1927//!
···7785//!}
7886//! ```
7987//!
8080-//! ## Client options:
8888+//!
8989+//! ## Component crates
9090+//!
9191+//! Jacquard is split into several crates for modularity. The main `jacquard` crate
9292+//! re-exports most of the others, so you typically only need to depend on it directly.
9393+//!
9494+//! - [`jacquard-common`](https://docs.rs/jacquard-common/latest/jacquard_common/index.html) - AT Protocol types (DIDs, handles, at-URIs, NSIDs, TIDs, CIDs, etc.)
9595+//! - [`jacquard-api`](https://docs.rs/jacquard-api/latest/jacquard_api/index.html) - Generated API bindings from 646+ lexicon schemas
9696+//! - [`jacquard-axum`](https://docs.rs/jacquard-axum/latest/jacquard_axum/index.html) - Server-side XRPC handler extractors for Axum framework (not re-exported, depends on jacquard)
9797+//! - [`jacquard-oauth`](https://docs.rs/jacquard-oauth/latest/jacquard_oauth/index.html) - OAuth/DPoP flow implementation with session management
9898+//! - [`jacquard-identity`](https://docs.rs/jacquard-identity/latest/jacquard_identity/index.html) - Identity resolution (handle → DID, DID → Doc, OAuth metadata)
9999+//! - [`jacquard-lexicon`](https://docs.rs/jacquard-lexicon/latest/jacquard_lexicon/index.html) - Lexicon resolution, fetching, parsing and Rust code generation from schemas
100100+//! - [`jacquard-derive`](https://docs.rs/jacquard-derive/latest/jacquard_derive/index.html) - Macros (`#[lexicon]`, `#[open_union]`, `#[derive(IntoStatic)]`)
101101+//!
102102+//!
103103+//! ### A note on lifetimes
104104+//!
105105+//! You'll notice a bunch of lifetimes all over Jacquard types, examples, and so on. If you're newer
106106+//! to Rust or have simply avoided them, they're part of how Rust knows how long to keep something
107107+//! around before cleaning it up. They're not unique to Rust (C and C++ have the same concept
108108+//! internally) but Rust is perhaps the one language that makes them explicit, because they're part
109109+//! of how it validates that things are memory-safe, and being able to give information to the compiler
110110+//! about how long it can expect something to stick around lets the compiler reason out much more
111111+//! sophisticated things. [The Rust book](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html) has a section on them if you want a refresher.
112112+//!
113113+//! > On Jacquard types like [`CowStr`], a `'static` lifetime parameter is used to refer to the owned
114114+//! version of a type, in the same way `String` is the owned version of `&str`.
115115+//!
116116+//! This is somewhat in tension with the 'make things simpler' goal of the crate, but it is honestly
117117+//! pretty straightforward once you know the deal, and Jacquard provides a number of escape hatches
118118+//! and easy ways to work.
119119+//!
120120+//! Because explicit lifetimes are somewhat unique to Rust and are not something you may be used to
121121+//! thinking about, they can seem a bit scary to work with. Normally the compiler is pretty good at
122122+//! them, but Jacquard is [built around borrowed deserialization](https://docs.rs/jacquard-common/latest/jacquard_common/#working-with-lifetimes-and-zero-copy-deserialization) and types. This is for reasons of
123123+//! speed and efficiency, because borrowing from your source buffer saves copying the data around.
124124+//!
125125+//! However, it does mean that any Jacquard type that can borrow (not all of them do) is annotated
126126+//! with a lifetime, to confirm that all the borrowed bits are ["covariant"](https://doc.rust-lang.org/nomicon/subtyping.html), i.e. that they all live
127127+//! at least the same amount of time, and that lifetime matches or exceeds the lifetime of the data
128128+//! structure. This also imposes certain restrictions on deserialization. Namely the [`DeserializeOwned`](https://serde.rs/lifetimes.html)
129129+//! bound does not apply to almost any types in Jacquard. There is a [`deserialize_owned`] function
130130+//! which you can use in a serde `deserialize_with` attribute to help, but the general pattern is
131131+//! to do borrowed deserialization and then call [`.into_static()`] if you need ownership.
132132+//!
133133+//! ### Easy mode
134134+//!
135135+//! Easy mode for jacquard is to mostly just use `'static` for your lifetime params and derive/use
136136+//! [`.into_static()`] as needed. When writing, first see if you can get away with `Thing<'_>`
137137+//! and let the compiler infer. second-easiest after that is `Thing<'static>`, third-easiest is giving
138138+//! everything one lifetime, e.g. `fn foo<'a>(&'a self, thing: Thing<'a>) -> /* thing with lifetime 'a */`.
139139+//!
140140+//! When parsing the output of atproto API calls, you can call `.into_output()` on the `Response<R>`
141141+//! struct to get an owned version with a `'static` lifetime. When deserializing, do not use
142142+//! `from_writer()` type deserialization functions, or features like Axum's `Json` extractor, as they
143143+//! have DeserializeOwned bounds and cannot borrow from their buffer. Either use Jacquard's features
144144+//! to get an owned version or follow the same [patterns](https://whtwnd.com/nonbinary.computer/3m33efvsylz2s) it uses in your own code.
145145+//!
146146+//! ## Client options
81147//!
82148//! - Stateless XRPC: any `HttpClient` (e.g., `reqwest::Client`) implements `XrpcExt`,
83149//! which provides `xrpc(base: Url) -> XrpcCall` for per-request calls with
···112178//! base endpoint to the user's PDS on login/restore.
113179//! - Stateful client (OAuth): `OAuthClient<S, T>` and `OAuthSession<S, T>` where `S: ClientAuthStore` and
114180//! `T: OAuthResolver + HttpClient`. The client is used to authenticate, returning a session which handles authentication and token refresh internally.
115115-//! - `Agent<A: AgentSession>` Session abstracts over the above two options. Currently it is a thin wrapper, but this will be the thing that gets all the convenience helpers.
181181+//! - `Agent<A: AgentSession>` Session abstracts over the above two options and provides some useful convenience features via the [`AgentSessionExt`] trait.
116182//!
117183//! Per-request overrides (stateless)
118184//! ```no_run
···145211//! }
146212//! ```
147213//!
148148-//! ## Component Crates
149149-//!
150150-//! Jacquard is split into several crates for modularity. The main `jacquard` crate
151151-//! re-exports most of the others, so you typically only need to depend on it directly.
152152-//!
153153-//! - [`jacquard-common`] - AT Protocol types (DIDs, handles, at-URIs, NSIDs, TIDs, CIDs, etc.)
154154-//! - [`jacquard-api`] - Generated API bindings from 646+ lexicon schemas
155155-//! - [`jacquard-axum`] - Server-side XRPC handler extractors for Axum framework (not re-exported, depends on jacquard)
156156-//! - [`jacquard-oauth`] - OAuth/DPoP flow implementation with session management
157157-//! - [`jacquard-identity`] - Identity resolution (handle→DID, DID→Doc, OAuth metadata)
158158-//! - [`jacquard-lexicon`] - Lexicon resolution, fetching, parsing and Rust code generation from schemas
159159-//! - [`jacquard-derive`] - Macros (`#[lexicon]`, `#[open_union]`, `#[derive(IntoStatic)]`)
214214+//! [`deserialize_owned`]: crate::deserialize_owned
215215+//! [`AgentSessionExt`]: crate::client::AgentSessionExt
216216+//! [`.into_static()`]: IntoStatic
160217161218#![warn(missing_docs)]
162219···164221165222pub use common::*;
166223#[cfg(feature = "api")]
167167-/// If enabled, re-export the generated api crate
168224pub use jacquard_api as api;
169225pub use jacquard_common as common;
170226171227#[cfg(feature = "derive")]
172172-/// if enabled, reexport the attribute macros
173228pub use jacquard_derive::*;
174229175230pub use jacquard_identity as identity;
176231177177-/// OAuth usage helpers (discovery, PAR, token exchange)
178232pub use jacquard_oauth as oauth;