stripe/hyper/
client_builder.rs1use hyper::http::{HeaderValue, Uri};
2use stripe_client_core::{RequestStrategy, SharedConfigBuilder};
3use stripe_shared::{AccountId, ApplicationId};
4
5use crate::StripeError;
6use crate::hyper::client::Client;
7
8static DEFAULT_USER_AGENT: &str = concat!("Stripe/v1 RustBindings/", env!("CARGO_PKG_VERSION"));
9const DEFAULT_API_BASE: &str = "https://api.stripe.com/";
10
11#[derive(Clone, Debug)]
16pub struct ClientBuilder {
17 inner: SharedConfigBuilder,
18}
19
20impl ClientBuilder {
21 pub fn new(secret: impl Into<String>) -> Self {
23 Self { inner: SharedConfigBuilder::new(secret) }
24 }
25
26 pub fn client_id(mut self, client_id: ApplicationId) -> Self {
29 self.inner = self.inner.client_id(client_id);
30 self
31 }
32
33 pub fn account_id(mut self, account_id: AccountId) -> Self {
37 self.inner = self.inner.account_id(account_id);
38 self
39 }
40
41 pub fn request_strategy(mut self, strategy: RequestStrategy) -> Self {
43 self.inner = self.inner.request_strategy(strategy);
44 self
45 }
46
47 pub fn url(mut self, url: impl Into<String>) -> Self {
49 self.inner = self.inner.url(url);
50 self
51 }
52
53 pub fn app_info(
59 mut self,
60 name: impl Into<String>,
61 version: Option<String>,
62 url: Option<String>,
63 ) -> Self {
64 self.inner = self.inner.app_info(name, version, url);
65 self
66 }
67
68 fn try_into_config(self) -> Result<ClientConfig, StripeError> {
69 let api_base = if let Some(url) = self.inner.api_base {
70 Uri::try_from(url).map_err(|err| {
71 StripeError::ConfigError(format!("user-provided Stripe url is invalid: {err}"))
72 })?
73 } else {
74 Uri::from_static(DEFAULT_API_BASE)
75 };
76
77 let user_agent_header = if let Some(app_info_str) = self.inner.app_info_str {
78 HeaderValue::try_from(format!("{DEFAULT_USER_AGENT} {app_info_str}"))
79 .map_err(|_| cons_header_err("app_info"))?
80 } else {
81 HeaderValue::from_static(DEFAULT_USER_AGENT)
82 };
83
84 let mut secret = HeaderValue::try_from(format!("Bearer {}", self.inner.secret))
85 .map_err(|_| cons_header_err("secret"))?;
86 secret.set_sensitive(true);
87 Ok(ClientConfig {
88 stripe_version: HeaderValue::from_str(self.inner.stripe_version.as_str())
89 .expect("all stripe versions produce valid header values"),
90 user_agent: user_agent_header,
91 client_id: self
92 .inner
93 .client_id
94 .map(|id| HeaderValue::try_from(id.to_string()))
95 .transpose()
96 .map_err(|_| cons_header_err("client_id"))?,
97 account_id: self
98 .inner
99 .account_id
100 .map(|id| HeaderValue::try_from(id.to_string()))
101 .transpose()
102 .map_err(|_| cons_header_err("account_id"))?,
103 request_strategy: self.inner.request_strategy.unwrap_or(RequestStrategy::Once),
104 secret,
105 api_base,
106 })
107 }
108
109 pub fn build(self) -> Result<Client, StripeError> {
114 Ok(Client::from_config(self.try_into_config()?))
115 }
116
117 #[cfg(feature = "blocking")]
125 pub fn build_sync(self) -> Result<crate::hyper::blocking::Client, StripeError> {
126 Ok(crate::hyper::blocking::Client::from_async(self.build()?))
127 }
128}
129
130fn cons_header_err(config_name: &'static str) -> StripeError {
131 StripeError::ClientError(format!("`{config_name}` can only include visible ASCII characters"))
132}
133
134#[derive(Clone, Debug)]
137pub struct ClientConfig {
138 pub stripe_version: HeaderValue,
139 pub user_agent: HeaderValue,
140 pub client_id: Option<HeaderValue>,
141 pub account_id: Option<HeaderValue>,
142 pub request_strategy: RequestStrategy,
143 pub secret: HeaderValue,
145 pub api_base: Uri,
146}