From 56b0665149ada2fa4b79a5b7a0c6568080f1a402 Mon Sep 17 00:00:00 2001 From: Elaina <48662592+oestradiol@users.noreply.github.com> Date: Wed, 18 Sep 2024 23:30:58 -0300 Subject: [PATCH 1/9] chore: Bumping MSRV to 1.75 --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5299106e..f7435176 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.70.0" +channel = "1.75.0" components = ["clippy", "rustfmt"] From ee3d38bb6156d67677483e3426a32f334eaf5415 Mon Sep 17 00:00:00 2001 From: Elaina <48662592+oestradiol@users.noreply.github.com> Date: Wed, 18 Sep 2024 23:42:09 -0300 Subject: [PATCH 2/9] Formatting --- bsky-sdk/src/rich_text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bsky-sdk/src/rich_text.rs b/bsky-sdk/src/rich_text.rs index 5c6a7d17..f1783722 100644 --- a/bsky-sdk/src/rich_text.rs +++ b/bsky-sdk/src/rich_text.rs @@ -102,7 +102,7 @@ impl RichText { /// Get segments of the rich text. pub fn segments(&self) -> Vec { let Some(facets) = self.facets.as_ref() else { - return vec![RichTextSegment::new(&self.text, None)] + return vec![RichTextSegment::new(&self.text, None)]; }; let mut segments = Vec::new(); let (mut text_cursor, mut facet_cursor) = (0, 0); From 15d3aa75ab666f5575ac568c3af096f431ba51df Mon Sep 17 00:00:00 2001 From: Elaina <48662592+oestradiol@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:40:48 -0300 Subject: [PATCH 3/9] Bumping version on Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cb829c51..ffacaf5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ resolver = "2" [workspace.package] edition = "2021" -rust-version = "1.70" +rust-version = "1.75" repository = "https://github.com/sugyan/atrium" license = "MIT" keywords = ["atproto", "bluesky"] From d751f6a53529ad30886179e3c1551338e1454649 Mon Sep 17 00:00:00 2001 From: Elaina <48662592+oestradiol@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:31:48 -0300 Subject: [PATCH 4/9] Configuring matrix workflow --- .github/workflows/rust.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index acf3904b..041b33ac 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -21,13 +21,21 @@ jobs: strategy: fail-fast: false + matrix: + rust: + - 1.75.0 + - stable steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install Rust toolchain - uses: actions-rust-lang/setup-rust-toolchain@v1 + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + components: rustfmt, clippy # https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json - name: Install Rust Problem Matcher From d2b131b523986524a65e31154a5f55a2a7ece3b7 Mon Sep 17 00:00:00 2001 From: Elaina <48662592+oestradiol@users.noreply.github.com> Date: Thu, 19 Sep 2024 11:54:29 -0300 Subject: [PATCH 5/9] Bringing back actions-rust-lang/setup-rust-toolchain@v1 --- .github/workflows/rust.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 041b33ac..b6a42b93 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -31,9 +31,8 @@ jobs: uses: actions/checkout@v4 - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 + uses: actions-rust-lang/setup-rust-toolchain@v1 with: - profile: minimal toolchain: ${{ matrix.rust }} components: rustfmt, clippy From a4d6545f1b873178f286268730ba17d42d2fba5d Mon Sep 17 00:00:00 2001 From: Elaina <48662592+oestradiol@users.noreply.github.com> Date: Thu, 19 Sep 2024 12:00:34 -0300 Subject: [PATCH 6/9] Removing "Install Rust Problem Matcher" --- .github/rust.json | 33 --------------------------------- .github/workflows/rust.yml | 4 ---- 2 files changed, 37 deletions(-) delete mode 100644 .github/rust.json diff --git a/.github/rust.json b/.github/rust.json deleted file mode 100644 index 0b58b7bb..00000000 --- a/.github/rust.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "problemMatcher": [ - { - "owner": "rustfmt", - "severity": "warning", - "pattern": [ - { - "regexp": "^(Diff in (.+)) at line (\\d+):$", - "message": 1, - "file": 2, - "line": 3 - } - ] - }, - { - "owner": "clippy", - "pattern": [ - { - "regexp": "^(?:\\x1b\\[[\\d;]+m)*(warning|warn|error)(?:\\x1b\\[[\\d;]+m)*(\\[(.*)\\])?(?:\\x1b\\[[\\d;]+m)*:(?:\\x1b\\[[\\d;]+m)* ([^\\x1b]*)(?:\\x1b\\[[\\d;]+m)*$", - "severity": 1, - "message": 4, - "code": 3 - }, - { - "regexp": "^(?:\\x1b\\[[\\d;]+m)*\\s*(?:\\x1b\\[[\\d;]+m)*\\s*--> (?:\\x1b\\[[\\d;]+m)*(.*):(\\d*):(\\d*)(?:\\x1b\\[[\\d;]+m)*$", - "file": 1, - "line": 2, - "column": 3 - } - ] - } - ] -} \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b6a42b93..cb388147 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -36,10 +36,6 @@ jobs: toolchain: ${{ matrix.rust }} components: rustfmt, clippy - # https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json - - name: Install Rust Problem Matcher - run: echo "::add-matcher::.github/rust.json" - - name: Bump opt-level run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml From a5aed14b960b540e8907c5df351c3d78a1199d67 Mon Sep 17 00:00:00 2001 From: Elaina <48662592+oestradiol@users.noreply.github.com> Date: Wed, 18 Sep 2024 23:44:36 -0300 Subject: [PATCH 7/9] Removing dependency --- Cargo.lock | 16 ---------------- Cargo.toml | 4 ---- atrium-api/Cargo.toml | 1 - atrium-xrpc-client/Cargo.toml | 1 - atrium-xrpc/Cargo.toml | 1 - bsky-cli/Cargo.toml | 1 - bsky-sdk/Cargo.toml | 1 - 7 files changed, 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea0fa279..2abd3976 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,22 +117,10 @@ dependencies = [ "futures-core", ] -[[package]] -name = "async-trait" -version = "0.1.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.71", -] - [[package]] name = "atrium-api" version = "0.24.4" dependencies = [ - "async-trait", "atrium-xrpc", "atrium-xrpc-client", "bumpalo", @@ -170,7 +158,6 @@ dependencies = [ name = "atrium-xrpc" version = "0.11.3" dependencies = [ - "async-trait", "bumpalo", "http 1.1.0", "serde", @@ -185,7 +172,6 @@ dependencies = [ name = "atrium-xrpc-client" version = "0.5.6" dependencies = [ - "async-trait", "atrium-xrpc", "bumpalo", "futures", @@ -268,7 +254,6 @@ name = "bsky-cli" version = "0.1.22" dependencies = [ "anyhow", - "async-trait", "bsky-sdk", "chrono", "clap", @@ -283,7 +268,6 @@ name = "bsky-sdk" version = "0.1.9" dependencies = [ "anyhow", - "async-trait", "atrium-api", "atrium-xrpc-client", "chrono", diff --git a/Cargo.toml b/Cargo.toml index ffacaf5e..6a848b77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,10 +28,6 @@ atrium-xrpc = { version = "0.11.3", path = "atrium-xrpc" } atrium-xrpc-client = { version = "0.5.6", path = "atrium-xrpc-client" } bsky-sdk = { version = "0.1.9", path = "bsky-sdk" } -# async in traits -# Can be removed once MSRV is at least 1.75.0. -async-trait = "0.1.80" - # DAG-CBOR codec ipld-core = { version = "0.4.1", default-features = false, features = ["std"] } serde_ipld_dagcbor = { version = "0.6.0", default-features = false, features = ["std"] } diff --git a/atrium-api/Cargo.toml b/atrium-api/Cargo.toml index a4758f34..f82c8ce9 100644 --- a/atrium-api/Cargo.toml +++ b/atrium-api/Cargo.toml @@ -13,7 +13,6 @@ keywords.workspace = true [dependencies] atrium-xrpc.workspace = true -async-trait.workspace = true chrono = { workspace = true, features = ["serde"] } http.workspace = true ipld-core = { workspace = true, features = ["serde"] } diff --git a/atrium-xrpc-client/Cargo.toml b/atrium-xrpc-client/Cargo.toml index 94655010..8e2eaa92 100644 --- a/atrium-xrpc-client/Cargo.toml +++ b/atrium-xrpc-client/Cargo.toml @@ -12,7 +12,6 @@ license.workspace = true keywords.workspace = true [dependencies] -async-trait.workspace = true atrium-xrpc.workspace = true isahc = { workspace = true, optional = true } reqwest = { workspace = true, optional = true } diff --git a/atrium-xrpc/Cargo.toml b/atrium-xrpc/Cargo.toml index 2ecd0240..1daba4d9 100644 --- a/atrium-xrpc/Cargo.toml +++ b/atrium-xrpc/Cargo.toml @@ -12,7 +12,6 @@ license.workspace = true keywords.workspace = true [dependencies] -async-trait.workspace = true http.workspace = true serde = { workspace = true, features = ["derive"] } serde_html_form.workspace = true diff --git a/bsky-cli/Cargo.toml b/bsky-cli/Cargo.toml index 5cd65173..51a6d21c 100644 --- a/bsky-cli/Cargo.toml +++ b/bsky-cli/Cargo.toml @@ -12,7 +12,6 @@ keywords = ["atproto", "bluesky", "atrium", "cli"] [dependencies] anyhow.workspace = true -async-trait.workspace = true bsky-sdk.workspace = true chrono.workspace = true clap.workspace = true diff --git a/bsky-sdk/Cargo.toml b/bsky-sdk/Cargo.toml index 880294d7..e0184a11 100644 --- a/bsky-sdk/Cargo.toml +++ b/bsky-sdk/Cargo.toml @@ -13,7 +13,6 @@ keywords = ["atproto", "bluesky", "atrium", "sdk"] [dependencies] anyhow.workspace = true -async-trait.workspace = true atrium-api.workspace = true atrium-xrpc-client = { workspace = true, optional = true } chrono.workspace = true From 23206ec30f0952c43b80f6e6a6a9a18213b856a9 Mon Sep 17 00:00:00 2001 From: Elaina <48662592+oestradiol@users.noreply.github.com> Date: Wed, 18 Sep 2024 23:53:31 -0300 Subject: [PATCH 8/9] Refactoring code --- atrium-api/src/agent.rs | 3 - atrium-api/src/agent/inner.rs | 11 --- atrium-api/src/agent/store.rs | 11 ++- atrium-api/src/agent/store/memory.rs | 3 - atrium-xrpc-client/src/isahc.rs | 2 - atrium-xrpc-client/src/reqwest.rs | 3 - atrium-xrpc/src/lib.rs | 3 - atrium-xrpc/src/traits.rs | 129 +++++++++++++-------------- bsky-sdk/src/agent.rs | 2 - bsky-sdk/src/agent/builder.rs | 2 - bsky-sdk/src/agent/config.rs | 13 ++- bsky-sdk/src/agent/config/file.rs | 3 - bsky-sdk/src/lib.rs | 3 - bsky-sdk/src/record.rs | 24 ++--- 14 files changed, 83 insertions(+), 129 deletions(-) diff --git a/atrium-api/src/agent.rs b/atrium-api/src/agent.rs index f424a061..298284e4 100644 --- a/atrium-api/src/agent.rs +++ b/atrium-api/src/agent.rs @@ -168,7 +168,6 @@ mod tests { use crate::com::atproto::server::create_session::OutputData; use crate::did_doc::{DidDocument, Service, VerificationMethod}; use crate::types::TryIntoUnknown; - use async_trait::async_trait; use atrium_xrpc::HttpClient; use http::{HeaderMap, HeaderName, HeaderValue, Request, Response}; use std::collections::HashMap; @@ -189,8 +188,6 @@ mod tests { headers: Arc>>>, } - #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] - #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl HttpClient for MockClient { async fn send_http( &self, diff --git a/atrium-api/src/agent/inner.rs b/atrium-api/src/agent/inner.rs index 910924aa..2c7ee72e 100644 --- a/atrium-api/src/agent/inner.rs +++ b/atrium-api/src/agent/inner.rs @@ -2,7 +2,6 @@ use super::{Session, SessionStore}; use crate::did_doc::DidDocument; use crate::types::string::Did; use crate::types::TryFromUnknown; -use async_trait::async_trait; use atrium_xrpc::error::{Error, Result, XrpcErrorKind}; use atrium_xrpc::{HttpClient, OutputDataOrBytes, XrpcClient, XrpcRequest}; use http::{Method, Request, Response, Uri}; @@ -51,8 +50,6 @@ impl Clone for WrapperClient { } } -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl HttpClient for WrapperClient where S: Send + Sync, @@ -67,8 +64,6 @@ where } } -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl XrpcClient for WrapperClient where S: SessionStore + Send + Sync, @@ -231,8 +226,6 @@ where } } -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl HttpClient for Client where S: Send + Sync, @@ -247,8 +240,6 @@ where } } -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl XrpcClient for Client where S: SessionStore + Send + Sync, @@ -321,8 +312,6 @@ impl Store { } } -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl SessionStore for Store where S: SessionStore + Send + Sync, diff --git a/atrium-api/src/agent/store.rs b/atrium-api/src/agent/store.rs index 3d2b3397..b79a5aef 100644 --- a/atrium-api/src/agent/store.rs +++ b/atrium-api/src/agent/store.rs @@ -1,16 +1,15 @@ mod memory; +use std::future::Future; + pub use self::memory::MemorySessionStore; pub(crate) use super::Session; -use async_trait::async_trait; -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait SessionStore { #[must_use] - async fn get_session(&self) -> Option; + fn get_session(&self) -> impl Future> + Send; #[must_use] - async fn set_session(&self, session: Session); + fn set_session(&self, session: Session) -> impl Future + Send; #[must_use] - async fn clear_session(&self); + fn clear_session(&self) -> impl Future + Send; } diff --git a/atrium-api/src/agent/store/memory.rs b/atrium-api/src/agent/store/memory.rs index d2933495..05eedaaf 100644 --- a/atrium-api/src/agent/store/memory.rs +++ b/atrium-api/src/agent/store/memory.rs @@ -1,5 +1,4 @@ use super::{Session, SessionStore}; -use async_trait::async_trait; use std::sync::Arc; use tokio::sync::RwLock; @@ -8,8 +7,6 @@ pub struct MemorySessionStore { session: Arc>>, } -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl SessionStore for MemorySessionStore { async fn get_session(&self) -> Option { self.session.read().await.clone() diff --git a/atrium-xrpc-client/src/isahc.rs b/atrium-xrpc-client/src/isahc.rs index 6c3a0e51..41cfcebe 100644 --- a/atrium-xrpc-client/src/isahc.rs +++ b/atrium-xrpc-client/src/isahc.rs @@ -1,5 +1,4 @@ #![doc = "XrpcClient implementation for [isahc]"] -use async_trait::async_trait; use atrium_xrpc::http::{Request, Response}; use atrium_xrpc::{HttpClient, XrpcClient}; use isahc::{AsyncReadResponseExt, HttpClient as Client}; @@ -52,7 +51,6 @@ impl IsahcClientBuilder { } } -#[async_trait] impl HttpClient for IsahcClient { async fn send_http( &self, diff --git a/atrium-xrpc-client/src/reqwest.rs b/atrium-xrpc-client/src/reqwest.rs index 75aed6a2..4e42a52b 100644 --- a/atrium-xrpc-client/src/reqwest.rs +++ b/atrium-xrpc-client/src/reqwest.rs @@ -1,5 +1,4 @@ #![doc = "XrpcClient implementation for [reqwest]"] -use async_trait::async_trait; use atrium_xrpc::http::{Request, Response}; use atrium_xrpc::{HttpClient, XrpcClient}; use reqwest::Client; @@ -48,8 +47,6 @@ impl ReqwestClientBuilder { } } -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl HttpClient for ReqwestClient { async fn send_http( &self, diff --git a/atrium-xrpc/src/lib.rs b/atrium-xrpc/src/lib.rs index e90327be..884e3dc3 100644 --- a/atrium-xrpc/src/lib.rs +++ b/atrium-xrpc/src/lib.rs @@ -13,7 +13,6 @@ mod tests { use super::*; use crate::error::{XrpcError, XrpcErrorKind}; use crate::{HttpClient, XrpcClient}; - use async_trait::async_trait; use http::{Request, Response}; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; @@ -24,8 +23,6 @@ mod tests { body: Vec, } - #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] - #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl HttpClient for DummyClient { async fn send_http( &self, diff --git a/atrium-xrpc/src/traits.rs b/atrium-xrpc/src/traits.rs index 1ece8c9b..4726aa58 100644 --- a/atrium-xrpc/src/traits.rs +++ b/atrium-xrpc/src/traits.rs @@ -2,20 +2,18 @@ use crate::error::Error; use crate::error::{XrpcError, XrpcErrorKind}; use crate::types::{Header, NSID_REFRESH_SESSION}; use crate::{InputDataOrBytes, OutputDataOrBytes, XrpcRequest}; -use async_trait::async_trait; use http::{Method, Request, Response}; use serde::{de::DeserializeOwned, Serialize}; use std::fmt::Debug; +use std::future::Future; /// An abstract HTTP client. -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait HttpClient { /// Send an HTTP request and return the response. - async fn send_http( + fn send_http( &self, request: Request>, - ) -> core::result::Result>, Box>; + ) -> impl Future>, Box>> + Send; } type XrpcResult = core::result::Result, self::Error>; @@ -24,87 +22,88 @@ type XrpcResult = core::result::Result, self::Error String; /// Get the authentication token to use `Authorization` header. #[allow(unused_variables)] - async fn authentication_token(&self, is_refresh: bool) -> Option { - None + fn authentication_token(&self, is_refresh: bool) -> impl Future> + Send { + async { None } } /// Get the `atproto-proxy` header. - async fn atproto_proxy_header(&self) -> Option { - None + fn atproto_proxy_header(&self) -> impl Future> + Send { + async { None } } /// Get the `atproto-accept-labelers` header. - async fn atproto_accept_labelers_header(&self) -> Option> { - None + fn atproto_accept_labelers_header(&self) -> impl Future>> + Send { + async { None } } /// Send an XRPC request and return the response. - async fn send_xrpc(&self, request: &XrpcRequest) -> XrpcResult + fn send_xrpc(&self, request: &XrpcRequest) -> impl Future> + Send where P: Serialize + Send + Sync, I: Serialize + Send + Sync, O: DeserializeOwned + Send + Sync, E: DeserializeOwned + Send + Sync + Debug, + Self: Sync, { - let mut uri = format!("{}/xrpc/{}", self.base_uri(), request.nsid); - // Query parameters - if let Some(p) = &request.parameters { - serde_html_form::to_string(p).map(|qs| { - uri += "?"; - uri += &qs; - })?; - }; - let mut builder = Request::builder().method(&request.method).uri(&uri); - // Headers - if let Some(encoding) = &request.encoding { - builder = builder.header(Header::ContentType, encoding); - } - if let Some(token) = self - .authentication_token( - request.method == Method::POST && request.nsid == NSID_REFRESH_SESSION, - ) - .await - { - builder = builder.header(Header::Authorization, format!("Bearer {}", token)); - } - if let Some(proxy) = self.atproto_proxy_header().await { - builder = builder.header(Header::AtprotoProxy, proxy); - } - if let Some(accept_labelers) = self.atproto_accept_labelers_header().await { - builder = builder.header(Header::AtprotoAcceptLabelers, accept_labelers.join(", ")); - } - // Body - let body = if let Some(input) = &request.input { - match input { - InputDataOrBytes::Data(data) => serde_json::to_vec(&data)?, - InputDataOrBytes::Bytes(bytes) => bytes.clone(), + async { + let mut uri = format!("{}/xrpc/{}", self.base_uri(), request.nsid); + // Query parameters + if let Some(p) = &request.parameters { + serde_html_form::to_string(p).map(|qs| { + uri += "?"; + uri += &qs; + })?; + }; + let mut builder = Request::builder().method(&request.method).uri(&uri); + // Headers + if let Some(encoding) = &request.encoding { + builder = builder.header(Header::ContentType, encoding); } - } else { - Vec::new() - }; - // Send - let (parts, body) = - self.send_http(builder.body(body)?).await.map_err(Error::HttpClient)?.into_parts(); - if parts.status.is_success() { - if parts - .headers - .get(http::header::CONTENT_TYPE) - .and_then(|value| value.to_str().ok()) - .map_or(false, |content_type| content_type.starts_with("application/json")) + if let Some(token) = self + .authentication_token( + request.method == Method::POST && request.nsid == NSID_REFRESH_SESSION, + ) + .await { - Ok(OutputDataOrBytes::Data(serde_json::from_slice(&body)?)) + builder = builder.header(Header::Authorization, format!("Bearer {}", token)); + } + if let Some(proxy) = self.atproto_proxy_header().await { + builder = builder.header(Header::AtprotoProxy, proxy); + } + if let Some(accept_labelers) = self.atproto_accept_labelers_header().await { + builder = builder.header(Header::AtprotoAcceptLabelers, accept_labelers.join(", ")); + } + // Body + let body = if let Some(input) = &request.input { + match input { + InputDataOrBytes::Data(data) => serde_json::to_vec(&data)?, + InputDataOrBytes::Bytes(bytes) => bytes.clone(), + } + } else { + Vec::new() + }; + // Send + let (parts, body) = + self.send_http(builder.body(body)?).await.map_err(Error::HttpClient)?.into_parts(); + if parts.status.is_success() { + if parts + .headers + .get(http::header::CONTENT_TYPE) + .and_then(|value| value.to_str().ok()) + .map_or(false, |content_type| content_type.starts_with("application/json")) + { + Ok(OutputDataOrBytes::Data(serde_json::from_slice(&body)?)) + } else { + Ok(OutputDataOrBytes::Bytes(body)) + } } else { - Ok(OutputDataOrBytes::Bytes(body)) + Err(Error::XrpcResponse(XrpcError { + status: parts.status, + error: serde_json::from_slice::>(&body).ok(), + })) } - } else { - Err(Error::XrpcResponse(XrpcError { - status: parts.status, - error: serde_json::from_slice::>(&body).ok(), - })) } } } diff --git a/bsky-sdk/src/agent.rs b/bsky-sdk/src/agent.rs index f8664e44..e7030ddd 100644 --- a/bsky-sdk/src/agent.rs +++ b/bsky-sdk/src/agent.rs @@ -260,13 +260,11 @@ where #[cfg(test)] mod tests { use super::*; - use async_trait::async_trait; use atrium_api::agent::Session; #[derive(Clone)] struct NoopStore; - #[async_trait] impl SessionStore for NoopStore { async fn get_session(&self) -> Option { unimplemented!() diff --git a/bsky-sdk/src/agent/builder.rs b/bsky-sdk/src/agent/builder.rs index fa7af946..9e333181 100644 --- a/bsky-sdk/src/agent/builder.rs +++ b/bsky-sdk/src/agent/builder.rs @@ -103,7 +103,6 @@ impl Default for BskyAgentBuilder { #[cfg(test)] mod tests { use super::*; - use async_trait::async_trait; use atrium_api::agent::Session; use atrium_api::com::atproto::server::create_session::OutputData; @@ -125,7 +124,6 @@ mod tests { struct MockSessionStore; - #[async_trait] impl SessionStore for MockSessionStore { async fn get_session(&self) -> Option { Some(session()) diff --git a/bsky-sdk/src/agent/config.rs b/bsky-sdk/src/agent/config.rs index a118ae99..85454ac3 100644 --- a/bsky-sdk/src/agent/config.rs +++ b/bsky-sdk/src/agent/config.rs @@ -1,8 +1,9 @@ //! Configuration for the [`BskyAgent`](super::BskyAgent). mod file; +use std::future::Future; + use crate::error::{Error, Result}; -use async_trait::async_trait; use atrium_api::agent::Session; pub use file::FileStore; use serde::{Deserialize, Serialize}; @@ -46,20 +47,18 @@ impl Default for Config { } /// The trait for loading configuration data. -#[async_trait] pub trait Loader { /// Loads the configuration data. - async fn load( + fn load( &self, - ) -> core::result::Result>; + ) -> impl Future>> + Send; } /// The trait for saving configuration data. -#[async_trait] pub trait Saver { /// Saves the configuration data. - async fn save( + fn save( &self, config: &Config, - ) -> core::result::Result<(), Box>; + ) -> impl Future>> + Send; } diff --git a/bsky-sdk/src/agent/config/file.rs b/bsky-sdk/src/agent/config/file.rs index 725cc55c..eb547d28 100644 --- a/bsky-sdk/src/agent/config/file.rs +++ b/bsky-sdk/src/agent/config/file.rs @@ -1,6 +1,5 @@ use super::{Config, Loader, Saver}; use anyhow::anyhow; -use async_trait::async_trait; use std::path::{Path, PathBuf}; /// An implementation of [`Loader`] and [`Saver`] that reads and writes a configuration file. @@ -19,7 +18,6 @@ impl FileStore { } } -#[async_trait] impl Loader for FileStore { async fn load( &self, @@ -33,7 +31,6 @@ impl Loader for FileStore { } } -#[async_trait] impl Saver for FileStore { async fn save( &self, diff --git a/bsky-sdk/src/lib.rs b/bsky-sdk/src/lib.rs index 948ccbd7..583a87d1 100644 --- a/bsky-sdk/src/lib.rs +++ b/bsky-sdk/src/lib.rs @@ -15,7 +15,6 @@ pub use error::{Error, Result}; #[cfg(test)] mod tests { - use async_trait::async_trait; use atrium_api::xrpc::http::{Request, Response}; use atrium_api::xrpc::types::Header; use atrium_api::xrpc::{HttpClient, XrpcClient}; @@ -24,7 +23,6 @@ mod tests { pub struct MockClient; - #[async_trait] impl HttpClient for MockClient { async fn send_http( &self, @@ -44,7 +42,6 @@ mod tests { } } - #[async_trait] impl XrpcClient for MockClient { fn base_uri(&self) -> String { String::new() diff --git a/bsky-sdk/src/record.rs b/bsky-sdk/src/record.rs index 9b61c70a..3688b83b 100644 --- a/bsky-sdk/src/record.rs +++ b/bsky-sdk/src/record.rs @@ -1,9 +1,10 @@ //! Record operations. mod agent; +use std::future::Future; + use crate::error::{Error, Result}; use crate::BskyAgent; -use async_trait::async_trait; use atrium_api::agent::store::SessionStore; use atrium_api::com::atproto::repo::{ create_record, delete_record, get_record, list_records, put_record, @@ -11,28 +12,24 @@ use atrium_api::com::atproto::repo::{ use atrium_api::types::{Collection, LimitedNonZeroU8, TryIntoUnknown}; use atrium_api::xrpc::XrpcClient; -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait Record where T: XrpcClient + Send + Sync, S: SessionStore + Send + Sync, { - async fn list( + fn list( agent: &BskyAgent, cursor: Option, limit: Option>, - ) -> Result; - async fn get(agent: &BskyAgent, rkey: String) -> Result; - async fn put(self, agent: &BskyAgent, rkey: String) -> Result; - async fn create(self, agent: &BskyAgent) -> Result; - async fn delete(agent: &BskyAgent, rkey: String) -> Result; + ) -> impl Future> + Send; + fn get(agent: &BskyAgent, rkey: String) -> impl Future> + Send; + fn put(self, agent: &BskyAgent, rkey: String) -> impl Future> + Send; + fn create(self, agent: &BskyAgent) -> impl Future> + Send; + fn delete(agent: &BskyAgent, rkey: String) -> impl Future> + Send; } macro_rules! record_impl { ($collection:path, $record:path, $record_data:path) => { - #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] - #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Record for $record where T: XrpcClient + Send + Sync, @@ -150,8 +147,6 @@ macro_rules! record_impl { } } - #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] - #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Record for $record_data where T: XrpcClient + Send + Sync, @@ -277,7 +272,6 @@ mod tests { struct MockClient; - #[async_trait] impl HttpClient for MockClient { async fn send_http( &self, @@ -307,7 +301,6 @@ mod tests { } } - #[async_trait] impl XrpcClient for MockClient { fn base_uri(&self) -> String { String::new() @@ -316,7 +309,6 @@ mod tests { struct MockSessionStore; - #[async_trait] impl SessionStore for MockSessionStore { async fn get_session(&self) -> Option { Some( From 0858a2c8b1330cdf455fc72b3308b87563642275 Mon Sep 17 00:00:00 2001 From: Elaina <48662592+oestradiol@users.noreply.github.com> Date: Thu, 19 Sep 2024 11:36:48 -0300 Subject: [PATCH 9/9] Relaxing back the Send trait on return types for wasm --- Cargo.lock | 14 +++ Cargo.toml | 3 + atrium-api/Cargo.toml | 1 + atrium-api/src/agent/store.rs | 7 +- atrium-xrpc/Cargo.toml | 1 + atrium-xrpc/src/traits.rs | 162 +++++++++++++++++++++------------- bsky-sdk/Cargo.toml | 1 + bsky-sdk/src/agent/config.rs | 8 +- bsky-sdk/src/record.rs | 22 +++-- 9 files changed, 147 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2abd3976..4c23ae65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,6 +136,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "trait-variant", "wasm-bindgen-test", ] @@ -165,6 +166,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "trait-variant", "wasm-bindgen-test", ] @@ -279,6 +281,7 @@ dependencies = [ "thiserror", "tokio", "toml", + "trait-variant", "unicode-segmentation", ] @@ -2340,6 +2343,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "trait-variant" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + [[package]] name = "try-lock" version = "0.2.5" diff --git a/Cargo.toml b/Cargo.toml index 6a848b77..3e989e1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,3 +72,6 @@ mockito = "1.4" # WebAssembly wasm-bindgen-test = "0.3.41" bumpalo = "~3.14.0" + +# Code generation +trait-variant = "0.1.2" \ No newline at end of file diff --git a/atrium-api/Cargo.toml b/atrium-api/Cargo.toml index f82c8ce9..10d9cd91 100644 --- a/atrium-api/Cargo.toml +++ b/atrium-api/Cargo.toml @@ -23,6 +23,7 @@ serde_bytes.workspace = true serde_json.workspace = true thiserror.workspace = true tokio = { workspace = true, optional = true } +trait-variant.workspace = true [features] default = ["agent", "bluesky"] diff --git a/atrium-api/src/agent/store.rs b/atrium-api/src/agent/store.rs index b79a5aef..22bdcb37 100644 --- a/atrium-api/src/agent/store.rs +++ b/atrium-api/src/agent/store.rs @@ -5,11 +5,12 @@ use std::future::Future; pub use self::memory::MemorySessionStore; pub(crate) use super::Session; +#[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(Send))] pub trait SessionStore { #[must_use] - fn get_session(&self) -> impl Future> + Send; + fn get_session(&self) -> impl Future>; #[must_use] - fn set_session(&self, session: Session) -> impl Future + Send; + fn set_session(&self, session: Session) -> impl Future; #[must_use] - fn clear_session(&self) -> impl Future + Send; + fn clear_session(&self) -> impl Future; } diff --git a/atrium-xrpc/Cargo.toml b/atrium-xrpc/Cargo.toml index 1daba4d9..e8c551d3 100644 --- a/atrium-xrpc/Cargo.toml +++ b/atrium-xrpc/Cargo.toml @@ -17,6 +17,7 @@ serde = { workspace = true, features = ["derive"] } serde_html_form.workspace = true serde_json.workspace = true thiserror.workspace = true +trait-variant.workspace = true [dev-dependencies] tokio = { workspace = true, features = ["macros", "rt"] } diff --git a/atrium-xrpc/src/traits.rs b/atrium-xrpc/src/traits.rs index 4726aa58..f04e3176 100644 --- a/atrium-xrpc/src/traits.rs +++ b/atrium-xrpc/src/traits.rs @@ -8,12 +8,18 @@ use std::fmt::Debug; use std::future::Future; /// An abstract HTTP client. +#[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(Send))] pub trait HttpClient { /// Send an HTTP request and return the response. fn send_http( &self, request: Request>, - ) -> impl Future>, Box>> + Send; + ) -> impl Future< + Output = core::result::Result< + Response>, + Box, + >, + >; } type XrpcResult = core::result::Result, self::Error>; @@ -22,88 +28,120 @@ type XrpcResult = core::result::Result, self::Error String; /// Get the authentication token to use `Authorization` header. #[allow(unused_variables)] - fn authentication_token(&self, is_refresh: bool) -> impl Future> + Send { + fn authentication_token(&self, is_refresh: bool) -> impl Future> { async { None } } /// Get the `atproto-proxy` header. - fn atproto_proxy_header(&self) -> impl Future> + Send { + fn atproto_proxy_header(&self) -> impl Future> { async { None } } /// Get the `atproto-accept-labelers` header. - fn atproto_accept_labelers_header(&self) -> impl Future>> + Send { + fn atproto_accept_labelers_header(&self) -> impl Future>> { async { None } } /// Send an XRPC request and return the response. - fn send_xrpc(&self, request: &XrpcRequest) -> impl Future> + Send + #[cfg(not(target_arch = "wasm32"))] + fn send_xrpc( + &self, + request: &XrpcRequest, + ) -> impl Future> where P: Serialize + Send + Sync, I: Serialize + Send + Sync, O: DeserializeOwned + Send + Sync, E: DeserializeOwned + Send + Sync + Debug, + // This code is duplicated because of this trait bound. + // `Self` has to be `Sync` for `Future` to be `Send`. Self: Sync, { - async { - let mut uri = format!("{}/xrpc/{}", self.base_uri(), request.nsid); - // Query parameters - if let Some(p) = &request.parameters { - serde_html_form::to_string(p).map(|qs| { - uri += "?"; - uri += &qs; - })?; - }; - let mut builder = Request::builder().method(&request.method).uri(&uri); - // Headers - if let Some(encoding) = &request.encoding { - builder = builder.header(Header::ContentType, encoding); - } - if let Some(token) = self - .authentication_token( - request.method == Method::POST && request.nsid == NSID_REFRESH_SESSION, - ) - .await - { - builder = builder.header(Header::Authorization, format!("Bearer {}", token)); - } - if let Some(proxy) = self.atproto_proxy_header().await { - builder = builder.header(Header::AtprotoProxy, proxy); - } - if let Some(accept_labelers) = self.atproto_accept_labelers_header().await { - builder = builder.header(Header::AtprotoAcceptLabelers, accept_labelers.join(", ")); - } - // Body - let body = if let Some(input) = &request.input { - match input { - InputDataOrBytes::Data(data) => serde_json::to_vec(&data)?, - InputDataOrBytes::Bytes(bytes) => bytes.clone(), - } - } else { - Vec::new() - }; - // Send - let (parts, body) = - self.send_http(builder.body(body)?).await.map_err(Error::HttpClient)?.into_parts(); - if parts.status.is_success() { - if parts - .headers - .get(http::header::CONTENT_TYPE) - .and_then(|value| value.to_str().ok()) - .map_or(false, |content_type| content_type.starts_with("application/json")) - { - Ok(OutputDataOrBytes::Data(serde_json::from_slice(&body)?)) - } else { - Ok(OutputDataOrBytes::Bytes(body)) - } - } else { - Err(Error::XrpcResponse(XrpcError { - status: parts.status, - error: serde_json::from_slice::>(&body).ok(), - })) - } + send_xrpc(self, request) + } + #[cfg(target_arch = "wasm32")] + fn send_xrpc( + &self, + request: &XrpcRequest, + ) -> impl Future> + where + P: Serialize + Send + Sync, + I: Serialize + Send + Sync, + O: DeserializeOwned + Send + Sync, + E: DeserializeOwned + Send + Sync + Debug, + { + send_xrpc(self, request) + } +} + +#[inline(always)] +async fn send_xrpc( + client: &C, + request: &XrpcRequest, +) -> XrpcResult +where + P: Serialize + Send + Sync, + I: Serialize + Send + Sync, + O: DeserializeOwned + Send + Sync, + E: DeserializeOwned + Send + Sync + Debug, +{ + let mut uri = format!("{}/xrpc/{}", client.base_uri(), request.nsid); + // Query parameters + if let Some(p) = &request.parameters { + serde_html_form::to_string(p).map(|qs| { + uri += "?"; + uri += &qs; + })?; + }; + let mut builder = Request::builder().method(&request.method).uri(&uri); + // Headers + if let Some(encoding) = &request.encoding { + builder = builder.header(Header::ContentType, encoding); + } + if let Some(token) = client + .authentication_token( + request.method == Method::POST && request.nsid == NSID_REFRESH_SESSION, + ) + .await + { + builder = builder.header(Header::Authorization, format!("Bearer {}", token)); + } + if let Some(proxy) = client.atproto_proxy_header().await { + builder = builder.header(Header::AtprotoProxy, proxy); + } + if let Some(accept_labelers) = client.atproto_accept_labelers_header().await { + builder = builder.header(Header::AtprotoAcceptLabelers, accept_labelers.join(", ")); + } + // Body + let body = if let Some(input) = &request.input { + match input { + InputDataOrBytes::Data(data) => serde_json::to_vec(&data)?, + InputDataOrBytes::Bytes(bytes) => bytes.clone(), + } + } else { + Vec::new() + }; + // Send + let (parts, body) = + client.send_http(builder.body(body)?).await.map_err(Error::HttpClient)?.into_parts(); + if parts.status.is_success() { + if parts + .headers + .get(http::header::CONTENT_TYPE) + .and_then(|value| value.to_str().ok()) + .map_or(false, |content_type| content_type.starts_with("application/json")) + { + Ok(OutputDataOrBytes::Data(serde_json::from_slice(&body)?)) + } else { + Ok(OutputDataOrBytes::Bytes(body)) } + } else { + Err(Error::XrpcResponse(XrpcError { + status: parts.status, + error: serde_json::from_slice::>(&body).ok(), + })) } } diff --git a/bsky-sdk/Cargo.toml b/bsky-sdk/Cargo.toml index e0184a11..1761eaac 100644 --- a/bsky-sdk/Cargo.toml +++ b/bsky-sdk/Cargo.toml @@ -23,6 +23,7 @@ serde_json.workspace = true thiserror.workspace = true toml = { version = "0.8.13", optional = true } unicode-segmentation = { version = "1.11.0", optional = true } +trait-variant.workspace = true [dev-dependencies] ipld-core.workspace = true diff --git a/bsky-sdk/src/agent/config.rs b/bsky-sdk/src/agent/config.rs index 85454ac3..a804e729 100644 --- a/bsky-sdk/src/agent/config.rs +++ b/bsky-sdk/src/agent/config.rs @@ -51,7 +51,9 @@ pub trait Loader { /// Loads the configuration data. fn load( &self, - ) -> impl Future>> + Send; + ) -> impl Future< + Output = core::result::Result>, + > + Send; } /// The trait for saving configuration data. @@ -60,5 +62,7 @@ pub trait Saver { fn save( &self, config: &Config, - ) -> impl Future>> + Send; + ) -> impl Future< + Output = core::result::Result<(), Box>, + > + Send; } diff --git a/bsky-sdk/src/record.rs b/bsky-sdk/src/record.rs index 3688b83b..81776e6a 100644 --- a/bsky-sdk/src/record.rs +++ b/bsky-sdk/src/record.rs @@ -12,6 +12,7 @@ use atrium_api::com::atproto::repo::{ use atrium_api::types::{Collection, LimitedNonZeroU8, TryIntoUnknown}; use atrium_api::xrpc::XrpcClient; +#[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(Send))] pub trait Record where T: XrpcClient + Send + Sync, @@ -21,11 +22,22 @@ where agent: &BskyAgent, cursor: Option, limit: Option>, - ) -> impl Future> + Send; - fn get(agent: &BskyAgent, rkey: String) -> impl Future> + Send; - fn put(self, agent: &BskyAgent, rkey: String) -> impl Future> + Send; - fn create(self, agent: &BskyAgent) -> impl Future> + Send; - fn delete(agent: &BskyAgent, rkey: String) -> impl Future> + Send; + ) -> impl Future>; + fn get( + agent: &BskyAgent, + rkey: String, + ) -> impl Future>; + fn put( + self, + agent: &BskyAgent, + rkey: String, + ) -> impl Future>; + fn create(self, agent: &BskyAgent) + -> impl Future>; + fn delete( + agent: &BskyAgent, + rkey: String, + ) -> impl Future>; } macro_rules! record_impl {