#![allow(dead_code, unused_variables)] maybe_async_cfg::content!{ #![maybe_async_cfg::default( idents( InnerClient, ServiceClient, ) )] type Response = String; type Url = &'static str; type Method = String; /// To use `maybe-async-cfg`, we must know which block of codes is only used on /// blocking implementation, and which on async. These two implementation should /// share the same API except for async/await keywords. /// This will generate two traits: `InnerClientSync` and `InnerClientAsync` #[maybe_async_cfg::maybe( sync(feature = "is_sync"), async(feature = "is_async", async_trait::async_trait), )] trait InnerClient { async fn request(method: Method, url: Url, data: String) -> Response; #[inline] async fn post(url: Url, data: String) -> Response { Self::request(String::from("post"), url, data).await } #[inline] async fn delete(url: Url, data: String) -> Response { Self::request(String::from("delete"), url, data).await } } /// This will generate a `ServiceClientSync`, which will implement /// `InnerClientSync`, and a `ServiceClientAsync`, which will implement /// `InnerClientAsync`. /// /// If we had a single `ServiceClient` which implemented both `InnerClientSync` /// and `InnerClientAsync`, calls to methods like `request` would be ambiguous /// when both async and sync were enabled. #[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(feature = "is_async"))] pub struct ServiceClient; /// Synchronous implementation. #[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(feature = "is_async"))] #[maybe_async_cfg::only_if(sync)] impl InnerClient for ServiceClient { fn request(method: Method, url: Url, data: String) -> Response { // your implementation for sync, like use `reqwest::blocking` to send // the request String::from("pretend we have a response") } } /// Asynchronous implementation only. #[maybe_async_cfg::maybe( sync(feature = "is_sync"), async(feature = "is_async", async_trait::async_trait), )] #[maybe_async_cfg::only_if(async)] impl InnerClient for ServiceClient { async fn request(method: Method, url: Url, data: String) -> Response { // your implementation for async, like use `reqwest::client` or // `async_std` to send the request String::from("pretend we have a response") } } /// Code of upstream API are almost the same for sync and async, except for /// async/await keyword. This will generate the same `impl` but for both /// `ServiceClientAsync` and `ServiceClientSync`. #[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(feature = "is_async"))] impl ServiceClient { async fn create_bucket(name: String) -> Response { Self::post("http://correct_url4create", String::from("my_bucket")).await // When `is_sync` is toggle on, this block will compiles to: // Self::post("http://correct_url4create", String::from("my_bucket")) } async fn delete_bucket(name: String) -> Response { Self::delete("http://correct_url4delete", String::from("my_bucket")).await } // and another thousands of functions that interact with service side } #[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(feature = "is_async"))] #[maybe_async_cfg::only_if(sync)] fn run_sync() { println!("sync impl running"); let _ = ServiceClientSync::create_bucket("bucket".to_owned()); } #[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(feature = "is_async"))] #[maybe_async_cfg::only_if(async)] async fn run_async() { println!("async impl running"); let _ = ServiceClientAsync::create_bucket("bucket".to_owned()).await; } #[tokio::main] async fn main() { #[cfg(feature = "is_sync")] run_sync(); #[cfg(feature = "is_async")] run_async().await; } }