diff --git a/Cargo.lock b/Cargo.lock index 2ddca2d3..84acde96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,35 +123,10 @@ dependencies = [ "futures-core", ] -[[package]] -name = "async-compression" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" -dependencies = [ - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", -] - -[[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.5" dependencies = [ - "async-trait", "atrium-xrpc", "atrium-xrpc-client", "bumpalo", @@ -167,6 +142,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "trait-variant", "wasm-bindgen-test", ] @@ -189,7 +165,6 @@ dependencies = [ name = "atrium-xrpc" version = "0.11.4" dependencies = [ - "async-trait", "bumpalo", "http 1.1.0", "serde", @@ -197,6 +172,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "trait-variant", "wasm-bindgen-test", ] @@ -204,7 +180,6 @@ dependencies = [ name = "atrium-xrpc-client" version = "0.5.7" dependencies = [ - "async-trait", "atrium-xrpc", "bumpalo", "futures", @@ -287,7 +262,6 @@ name = "bsky-cli" version = "0.1.23" dependencies = [ "anyhow", - "async-trait", "bsky-sdk", "chrono", "clap", @@ -302,7 +276,6 @@ name = "bsky-sdk" version = "0.1.10" dependencies = [ "anyhow", - "async-trait", "atrium-api", "atrium-xrpc-client", "chrono", @@ -314,6 +287,7 @@ dependencies = [ "thiserror", "tokio", "toml", + "trait-variant", "unicode-segmentation", ] @@ -2405,6 +2379,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 b6b8cc75..59ab8288 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,10 +28,6 @@ atrium-xrpc = { version = "0.11.4", path = "atrium-xrpc" } atrium-xrpc-client = { version = "0.5.7", path = "atrium-xrpc-client" } bsky-sdk = { version = "0.1.10", 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"] } @@ -76,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 36f8ba43..0c3b0b75 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"] } @@ -24,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.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..22bdcb37 100644 --- a/atrium-api/src/agent/store.rs +++ b/atrium-api/src/agent/store.rs @@ -1,16 +1,16 @@ 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)] +#[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(Send))] pub trait SessionStore { #[must_use] - async fn get_session(&self) -> Option; + fn get_session(&self) -> impl Future>; #[must_use] - async fn set_session(&self, session: Session); + fn set_session(&self, session: Session) -> impl Future; #[must_use] - async fn clear_session(&self); + fn clear_session(&self) -> impl Future; } 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/Cargo.toml b/atrium-xrpc-client/Cargo.toml index 46752081..65e8e06c 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-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/Cargo.toml b/atrium-xrpc/Cargo.toml index aab9054a..f969b7e3 100644 --- a/atrium-xrpc/Cargo.toml +++ b/atrium-xrpc/Cargo.toml @@ -12,12 +12,12 @@ license.workspace = true keywords.workspace = true [dependencies] -async-trait.workspace = true http.workspace = true 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/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..f04e3176 100644 --- a/atrium-xrpc/src/traits.rs +++ b/atrium-xrpc/src/traits.rs @@ -2,20 +2,24 @@ 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)] +#[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(Send))] 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< + Output = core::result::Result< + Response>, + Box, + >, + >; } type XrpcResult = core::result::Result, self::Error>; @@ -24,87 +28,120 @@ 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> { + async { None } } /// Get the `atproto-proxy` header. - async fn atproto_proxy_header(&self) -> Option { - None + fn atproto_proxy_header(&self) -> impl Future> { + 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>> { + async { None } } /// Send an XRPC request and return the response. - async fn send_xrpc(&self, request: &XrpcRequest) -> XrpcResult + #[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, { - 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); + 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(), } - if let Some(token) = self - .authentication_token( - request.method == Method::POST && request.nsid == NSID_REFRESH_SESSION, - ) - .await + } 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")) { - 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)) - } + Ok(OutputDataOrBytes::Data(serde_json::from_slice(&body)?)) } else { - Err(Error::XrpcResponse(XrpcError { - status: parts.status, - error: serde_json::from_slice::>(&body).ok(), - })) + Ok(OutputDataOrBytes::Bytes(body)) } + } else { + Err(Error::XrpcResponse(XrpcError { + status: parts.status, + error: serde_json::from_slice::>(&body).ok(), + })) } } diff --git a/bsky-cli/Cargo.toml b/bsky-cli/Cargo.toml index fd460170..e4d22b0d 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 9344011d..8a6be27e 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 @@ -24,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.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..a804e729 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,22 @@ 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< + Output = core::result::Result>, + > + 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< + Output = core::result::Result<(), Box>, + > + 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..81776e6a 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,36 @@ 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)] +#[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(Send))] 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>; + 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 { ($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 +159,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 +284,6 @@ mod tests { struct MockClient; - #[async_trait] impl HttpClient for MockClient { async fn send_http( &self, @@ -307,7 +313,6 @@ mod tests { } } - #[async_trait] impl XrpcClient for MockClient { fn base_uri(&self) -> String { String::new() @@ -316,7 +321,6 @@ mod tests { struct MockSessionStore; - #[async_trait] impl SessionStore for MockSessionStore { async fn get_session(&self) -> Option { Some(