summaryrefslogtreecommitdiff
path: root/examples/service_client.rs
blob: 3db9d2b4a7e50fed40bc62c811cc9ef7a83e2ee2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#![allow(dead_code, unused_variables)]
/// To use `maybe-async`, 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, and use `sync_impl` and
/// `async_impl` to mark these implementation.
type Response = String;
type Url = &'static str;
type Method = String;

/// InnerClient are used to actually send request,
/// which differ a lot between sync and async.
///
/// Use native async function in trait
#[maybe_async::maybe_async(AFIT)]
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
    }
}

/// The higher level API for end user.
pub struct ServiceClient;

/// Synchronous  implementation, only compiles when `is_sync` feature is off.
/// Else the compiler will complain that *request is defined multiple times* and
/// blabla.
#[maybe_async::sync_impl]
impl InnerClient for ServiceClient {
    fn request(method: Method, url: Url, data: String) -> Response {
        // your implementation for sync, like use
        // `reqwest::blocking` to send request
        String::from("pretend we have a response")
    }
}

/// Asynchronous implementation, only compiles when `is_sync` feature is off.
#[maybe_async::async_impl(AFIT)]
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 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.
impl ServiceClient {
    #[maybe_async::maybe_async]
    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"))
    }
    #[maybe_async::maybe_async]
    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::sync_impl]
fn main() {
    let _ = ServiceClient::create_bucket("bucket".to_owned());
}

#[maybe_async::async_impl]
#[tokio::main]
async fn main() {
    let _ = ServiceClient::create_bucket("bucket".to_owned()).await;
}