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
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use crate::client::r#async::Client as AsyncClient;
use crate::error::Error;
use crate::params::Headers;
use serde::de::DeserializeOwned;
use std::cell::RefCell;
use std::sync::Arc;
use std::time::Duration;

/// The delay after which the blocking `Client` will assume the request has failed.
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);

pub type Response<T> = Result<T, Error>;

#[inline(always)]
pub(crate) fn ok<T>(ok: T) -> Response<T> {
    Ok(ok)
}

#[inline(always)]
pub(crate) fn err<T>(err: crate::Error) -> Response<T> {
    Err(err)
}

#[derive(Clone)]
pub struct Client {
    inner: AsyncClient,
    runtime: Arc<RefCell<tokio::runtime::Runtime>>,
}

impl Client {
    /// Creates a new client pointed to `https://api.stripe.com/`
    pub fn new(secret_key: impl Into<String>) -> Client {
        Client::from_async(AsyncClient::new(secret_key))
    }

    /// Creates a new client posted to a custom `scheme://host/`
    pub fn from_url(scheme_host: impl Into<String>, secret_key: impl Into<String>) -> Client {
        Client::from_async(AsyncClient::from_url(scheme_host, secret_key))
    }

    fn from_async(inner: AsyncClient) -> Client {
        let runtime = tokio::runtime::Builder::new()
            .enable_io()
            .enable_time() // use separate `io/time` instead of `all` to ensure `tokio/time` is enabled
            .basic_scheduler()
            .build()
            .unwrap();
        Client { inner, runtime: Arc::new(RefCell::new(runtime)) }
    }

    /// Clones a new client with different headers.
    ///
    /// This is the recommended way to send requests for many different Stripe accounts
    /// or with different Meta, Extra, and Expand headers while using the same secret key.
    pub fn with_headers(&self, headers: Headers) -> Client {
        Client { inner: self.inner.with_headers(headers), runtime: self.runtime.clone() }
    }

    pub fn set_app_info(&mut self, name: String, version: Option<String>, url: Option<String>) {
        self.inner.set_app_info(name, version, url);
    }

    /// Sets a value for the Stripe-Account header
    ///
    /// This is recommended if you are acting as only one Account for the lifetime of the client.
    /// Otherwise, prefer `client.with(Headers{stripe_account: "acct_ABC", ..})`.
    pub fn set_stripe_account(&mut self, account_id: impl Into<String>) {
        self.inner.set_stripe_account(account_id)
    }

    /// Make a `GET` http request with just a path
    pub fn get<T: DeserializeOwned + Send + 'static>(&self, path: &str) -> Response<T> {
        self.send_blocking(self.inner.get(path))
    }

    /// Make a `GET` http request with url query parameters
    pub fn get_query<T: DeserializeOwned + Send + 'static, P: serde::Serialize>(
        &self,
        path: &str,
        params: P,
    ) -> Response<T> {
        self.send_blocking(self.inner.get_query(path, params))
    }

    /// Make a `DELETE` http request with just a path
    pub fn delete<T: DeserializeOwned + Send + 'static>(&self, path: &str) -> Response<T> {
        self.send_blocking(self.inner.delete(path))
    }

    /// Make a `DELETE` http request with url query parameters
    pub fn delete_query<T: DeserializeOwned + Send + 'static, P: serde::Serialize>(
        &self,
        path: &str,
        params: P,
    ) -> Response<T> {
        self.send_blocking(self.inner.delete_query(path, params))
    }

    /// Make a `POST` http request with just a path
    pub fn post<T: DeserializeOwned + Send + 'static>(&self, path: &str) -> Response<T> {
        self.send_blocking(self.inner.post(path))
    }

    /// Make a `POST` http request with urlencoded body
    pub fn post_form<T: DeserializeOwned + Send + 'static, F: serde::Serialize>(
        &self,
        path: &str,
        form: F,
    ) -> Response<T> {
        self.send_blocking(self.inner.post_form(path, form))
    }

    fn send_blocking<T: DeserializeOwned + Send + 'static>(
        &self,
        request: super::r#async::Response<T>,
    ) -> Response<T> {
        match self.runtime.borrow_mut().block_on(async {
            // N.B. The `tokio::time::timeout` must be called from within a running async
            //      context or else it will panic (it registers with the thread-local timer).
            tokio::time::timeout(DEFAULT_TIMEOUT, request).await
        }) {
            Ok(finished) => finished,
            Err(_) => Err(Error::timeout()),
        }
    }
}