A better Rust ATProto crate

edit pass on changelog, improving readme

+128 -65
+6 -38
CHANGELOG.md
··· 2 2 3 3 ## [0.5.0] - 2025-10-13 4 4 5 - ### Breaking Changes 6 - 7 - **AgentSession trait** (`jacquard`) 8 - - Removed `async fn` in favour of `impl Future` return types for better trait object compatibility 9 - - Methods now return `impl Future` instead of being marked `async fn` 10 - 11 - **XRPC improvements** (`jacquard-common`) 12 - - Simplified response transmutation for typed record retrieval 13 - - `Response::transmute()` added for zero-cost response type conversion 14 - 15 - **jacquard-axum** 16 - - Removed binary target (`main.rs`), now library-only 17 - 18 5 ### Added 19 6 20 7 **Agent convenience methods** (`jacquard`) ··· 22 9 - **Basic CRUD**: `create_record()`, `get_record()`, `put_record()`, `delete_record()` 23 10 - **Update patterns**: `update_record()` (fetch-modify-put), `update_vec()`, `update_vec_item()` 24 11 - **Blob operations**: `upload_blob()` 25 - - All methods auto-fill repo from session and collection from type's `Collection::NSID` 26 - - Simplified bounds on `update_record` - no HRTB issues, works with all record types 12 + - All methods auto-fill repo from session or URI parameter as relevant, and collection from type's `Collection::NSID` 27 13 28 14 **VecUpdate trait** (`jacquard`) 29 15 - `VecUpdate` trait for fetch-modify-put patterns on array-based endpoints 30 - - `PreferencesUpdate` implementation for updating user preferences 31 - - Enables type-safe updates to preferences, saved feeds, and other array endpoints 16 + - `PreferencesUpdate` implementation for updating Bluesky user preferences 17 + - Enables simpler updates to preferences and other 'array of union' types 32 18 33 - **Typed record retrieval** (`jacquard-api`, `jacquard-common`) 19 + **Typed record retrieval** (`jacquard-api`, `jacquard-common`, `jacquard-lexicon`) 34 20 - Each collection generates `{Type}Record` marker struct implementing `XrpcResp` 35 21 - `Collection::Record` associated type points to the marker 36 22 - `get_record::<R>()` returns `Response<R::Record>` with zero-copy `.parse()` ··· 42 28 - `post_with_image.rs`: Uploading images and creating posts with embeds 43 29 - `update_preferences.rs`: Using VecUpdate for preferences 44 30 - `create_whitewind_post.rs`, `read_whitewind_post.rs`: Third-party lexicons 45 - - `read_tangled_repo.rs`: Reading git repo metadata from tangled.sh 31 + - `read_tangled_repo.rs`: Reading git repo metadata from tangled.org 46 32 - `resolve_did.rs`: Identity resolution examples 47 33 - `public_atproto_feed.rs`: Unauthenticated feed access 48 34 - `axum_server.rs`: Server-side XRPC handler 49 35 50 - ### Changed 51 - 52 - **Code organization** (`jacquard-lexicon`) 53 - - Refactored monolithic `codegen.rs` into focused modules: 54 - - `codegen/structs.rs`: Record and object generation 55 - - `codegen/xrpc.rs`: XRPC request/response generation 56 - - `codegen/types.rs`: Type alias and union generation 57 - - `codegen/names.rs`: Identifier sanitization and naming 58 - - `codegen/lifetime.rs`: Lifetime propagation logic 59 - - `codegen/output.rs`: Module and feature generation 60 - - `codegen/utils.rs`: Shared utilities 61 - - Improved code navigation and maintainability 62 36 63 37 **Documentation** (`jacquard`) 64 - - Added comprehensive trait-level docs for `AgentSessionExt` 65 - - Updated examples to use new convenience methods 66 - 67 - ### Fixed 68 - 69 - - `update_record` now works with all record types without lifetime issues 70 - - Proper `IdentityResolver` bounds on `AgentSessionExt` 38 + - A whole host of examples added, as well as a lengthy explainer of the trait patterns. 71 39 72 40 ## [0.4.1] - 2025-10-13 73 41
+102 -15
Cargo.lock
··· 1761 1761 "bytes", 1762 1762 "clap", 1763 1763 "http", 1764 - "jacquard-api", 1765 - "jacquard-common", 1766 - "jacquard-derive", 1767 - "jacquard-identity", 1764 + "jacquard-api 0.4.1", 1765 + "jacquard-common 0.5.0", 1766 + "jacquard-derive 0.5.0", 1767 + "jacquard-identity 0.4.1", 1768 1768 "jacquard-oauth", 1769 1769 "jose-jwk", 1770 1770 "miette", ··· 1789 1789 dependencies = [ 1790 1790 "bon", 1791 1791 "bytes", 1792 - "jacquard-common", 1793 - "jacquard-derive", 1792 + "jacquard-common 0.5.0", 1793 + "jacquard-derive 0.5.0", 1794 + "miette", 1795 + "serde", 1796 + "thiserror 2.0.17", 1797 + ] 1798 + 1799 + [[package]] 1800 + name = "jacquard-api" 1801 + version = "0.4.1" 1802 + source = "git+https://tangled.org/@nonbinary.computer/jacquard#8c229615c802488f3310f1cb35e7b79683289893" 1803 + dependencies = [ 1804 + "bon", 1805 + "bytes", 1806 + "jacquard-common 0.5.0 (git+https://tangled.org/@nonbinary.computer/jacquard)", 1807 + "jacquard-derive 0.5.0 (git+https://tangled.org/@nonbinary.computer/jacquard)", 1794 1808 "miette", 1795 1809 "serde", 1796 1810 "thiserror 2.0.17", ··· 1805 1819 "axum-test", 1806 1820 "bytes", 1807 1821 "jacquard", 1808 - "jacquard-common", 1822 + "jacquard-common 0.5.0", 1809 1823 "miette", 1810 1824 "serde", 1811 1825 "serde_html_form", ··· 1855 1869 ] 1856 1870 1857 1871 [[package]] 1872 + name = "jacquard-common" 1873 + version = "0.5.0" 1874 + source = "git+https://tangled.org/@nonbinary.computer/jacquard#8c229615c802488f3310f1cb35e7b79683289893" 1875 + dependencies = [ 1876 + "async-trait", 1877 + "base64 0.22.1", 1878 + "bon", 1879 + "bytes", 1880 + "chrono", 1881 + "cid", 1882 + "http", 1883 + "ipld-core", 1884 + "langtag", 1885 + "miette", 1886 + "multibase", 1887 + "multihash", 1888 + "ouroboros", 1889 + "rand 0.9.2", 1890 + "regex", 1891 + "reqwest", 1892 + "serde", 1893 + "serde_html_form", 1894 + "serde_ipld_dagcbor", 1895 + "serde_json", 1896 + "serde_with", 1897 + "smol_str", 1898 + "thiserror 2.0.17", 1899 + "tokio", 1900 + "trait-variant", 1901 + "url", 1902 + ] 1903 + 1904 + [[package]] 1858 1905 name = "jacquard-derive" 1859 1906 version = "0.5.0" 1860 1907 dependencies = [ 1861 1908 "heck 0.5.0", 1862 1909 "itertools", 1863 - "jacquard-common", 1910 + "jacquard-common 0.5.0", 1911 + "prettyplease", 1912 + "proc-macro2", 1913 + "quote", 1914 + "serde", 1915 + "serde_json", 1916 + "serde_repr", 1917 + "serde_with", 1918 + "syn 2.0.106", 1919 + ] 1920 + 1921 + [[package]] 1922 + name = "jacquard-derive" 1923 + version = "0.5.0" 1924 + source = "git+https://tangled.org/@nonbinary.computer/jacquard#8c229615c802488f3310f1cb35e7b79683289893" 1925 + dependencies = [ 1926 + "heck 0.5.0", 1927 + "itertools", 1864 1928 "prettyplease", 1865 1929 "proc-macro2", 1866 1930 "quote", ··· 1880 1944 "bytes", 1881 1945 "hickory-resolver", 1882 1946 "http", 1883 - "jacquard-api", 1884 - "jacquard-common", 1947 + "jacquard-api 0.4.1", 1948 + "jacquard-common 0.5.0", 1949 + "miette", 1950 + "percent-encoding", 1951 + "reqwest", 1952 + "serde", 1953 + "serde_html_form", 1954 + "serde_json", 1955 + "thiserror 2.0.17", 1956 + "tokio", 1957 + "url", 1958 + "urlencoding", 1959 + ] 1960 + 1961 + [[package]] 1962 + name = "jacquard-identity" 1963 + version = "0.4.1" 1964 + source = "git+https://tangled.org/@nonbinary.computer/jacquard#8c229615c802488f3310f1cb35e7b79683289893" 1965 + dependencies = [ 1966 + "async-trait", 1967 + "bon", 1968 + "bytes", 1969 + "http", 1970 + "jacquard-api 0.4.1 (git+https://tangled.org/@nonbinary.computer/jacquard)", 1971 + "jacquard-common 0.5.0 (git+https://tangled.org/@nonbinary.computer/jacquard)", 1885 1972 "miette", 1886 1973 "percent-encoding", 1887 1974 "reqwest", ··· 1903 1990 "glob", 1904 1991 "heck 0.5.0", 1905 1992 "itertools", 1906 - "jacquard-api", 1907 - "jacquard-common", 1908 - "jacquard-identity", 1993 + "jacquard-api 0.4.1 (git+https://tangled.org/@nonbinary.computer/jacquard)", 1994 + "jacquard-common 0.5.0 (git+https://tangled.org/@nonbinary.computer/jacquard)", 1995 + "jacquard-identity 0.4.1 (git+https://tangled.org/@nonbinary.computer/jacquard)", 1909 1996 "kdl", 1910 1997 "miette", 1911 1998 "prettyplease", ··· 1934 2021 "dashmap", 1935 2022 "elliptic-curve", 1936 2023 "http", 1937 - "jacquard-common", 1938 - "jacquard-identity", 2024 + "jacquard-common 0.5.0", 2025 + "jacquard-identity 0.4.1", 1939 2026 "jose-jwa", 1940 2027 "jose-jwk", 1941 2028 "miette",
+20 -12
README.md
··· 1 + [![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) 2 + 1 3 # Jacquard 2 4 3 - A suite of Rust crates for the AT Protocol. [Docs](https://docs.rs/jacquard/latest/jacquard/) 5 + A suite of Rust crates intended to make it much easier to get started with atproto development, without sacrificing flexibility or performance. 6 + 7 + [Jacquard is simpler](https://whtwnd.com/nonbinary.computer/3m33efvsylz2s) because it is designed in a way which makes things simple that almost every other atproto library seems to make difficult. 8 + 9 + It is also designed around zero-copy/borrowed deserialization: types like [`Post<'_>`](https://tangled.org/@nonbinary.computer/jacquard/blob/main/crates/jacquard-api/src/app_bsky/feed/post.rs) 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. 4 10 5 11 ## Goals and Features 6 12 ··· 8 14 - Batteries-included, but easily replaceable batteries. 9 15 - Easy to extend with custom lexicons using code generation or handwritten api types 10 16 - Straightforward OAuth 11 - - stateless options (or options where you handle the state) for rolling your own 12 - - all the building blocks of the convenient abstractions are available 13 - - lexicon Value type for working with unknown atproto data (dag-cbor or json) 14 - - order of magnitude less boilerplate than some existing crates 15 - - use as much or as little from the crates as you need 17 + - Stateless options (or options where you handle the state) for rolling your own 18 + - All the building blocks of the convenient abstractions are available 19 + - Server-side convenience features 20 + - Lexicon Data value type for working with unknown atproto data (dag-cbor or json) 21 + - An order of magnitude less boilerplate than some existing crates 22 + - Use as much or as little from the crates as you need 23 + 16 24 17 25 ## Example 18 26 ··· 75 83 76 84 ## Component crates 77 85 78 - Jacquard is broken up into several crates for modularity. The correct one to use is generally `jacquard` itself, as it re-exports the others. 86 + Jacquard is broken up into several crates for modularity. The correct one to use is generally `jacquard` itself, as it re-exports most of the others. 79 87 - `jacquard`: Main crate [![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) 80 88 - `jacquard-common`: Foundation crate [![Crates.io](https://img.shields.io/crates/v/jacquard-common.svg)](https://crates.io/crates/jacquard-common) [![Documentation](https://docs.rs/jacquard-common/badge.svg)](https://docs.rs/jacquard-common) 81 - - `jacquard-api`: Autogenerated API bindings [![Crates.io](https://img.shields.io/crates/v/jacquard-api.svg)](https://crates.io/crates/jacquard-api) [![Documentation](https://docs.rs/jacquard-api/badge.svg)](https://docs.rs/jacquard-api) 82 89 - `jacquard-axum`: Axum extractor and other helpers [![Crates.io](https://img.shields.io/crates/v/jacquard-axum.svg)](https://crates.io/crates/jacquard-axum) [![Documentation](https://docs.rs/jacquard-axum/badge.svg)](https://docs.rs/jacquard-axum) 90 + - `jacquard-api`: Autogenerated API bindings [![Crates.io](https://img.shields.io/crates/v/jacquard-api.svg)](https://crates.io/crates/jacquard-api) [![Documentation](https://docs.rs/jacquard-api/badge.svg)](https://docs.rs/jacquard-api) 83 91 - `jacquard-oauth`: atproto OAuth implementation [![Crates.io](https://img.shields.io/crates/v/jacquard-oauth.svg)](https://crates.io/crates/jacquard-oauth) [![Documentation](https://docs.rs/jacquard-oauth/badge.svg)](https://docs.rs/jacquard-oauth) 84 92 - `jacquard-identity`: Identity resolution [![Crates.io](https://img.shields.io/crates/v/jacquard-identity.svg)](https://crates.io/crates/jacquard-identity) [![Documentation](https://docs.rs/jacquard-identity/badge.svg)](https://docs.rs/jacquard-identity) 85 93 - `jacquard-lexicon`: Lexicon parsing and code generation [![Crates.io](https://img.shields.io/crates/v/jacquard-lexicon.svg)](https://crates.io/crates/jacquard-lexicon) [![Documentation](https://docs.rs/jacquard-lexicon/badge.svg)](https://docs.rs/jacquard-lexicon) ··· 91 99 92 100 Highlights: 93 101 94 - - A ton of new lexicons included in `jacquard-api` 95 - - `jacquard-axum` Axum extractor and a number of improvements to lifetimes and (de)serialization required to make that work (thanks [@thoth.ptnote.dev](https://tangled.org/@thoth.ptnote.dev) for helping provide feedback and sample code to test against) 96 - - `from_data`, `from_raw_data`, `to_data`, and `to_raw_data` to serialize to and deserialize from the loosely typed value data formats (think `serde_json::from_value` and company). Particularly useful for second-stage deserialization of type "unknown" fields in lexicons, such as `PostView.record`. 97 - - better API code generation 102 + - `AgentSessionExt` trait with a host of convenience methods for working with records and preferences 103 + - Improvements to the `Collection` trait, code generation, and addition of the `VecUpdate` trait to enable that 104 + - A bunch of examples, both in the docs and in the repository 105 + - More lexicons in the generated API bindings. 98 106 99 107 ## Development 100 108