use std::sync::Arc;
use std::time::Duration;
use crate::auth::credentials::REDACTED;
use crate::auth::{DirectCredentials, OAuth1Credential, OAuth2Credential, OnTokenRefreshed};
use crate::config::{DEFAULT_API_BASE_URL, DEFAULT_TOKEN_URL};
use crate::error::{Error, Result};
use super::source::CredentialSource;
use super::{Client, DEFAULT_TIMEOUT_SECS};
#[must_use = "a ClientBuilder does nothing until build() is called"]
pub struct ClientBuilder {
bearer: Option<String>,
oauth2: Option<OAuth2Credential>,
oauth1: Option<OAuth1Credential>,
hook: Option<Arc<dyn OnTokenRefreshed>>,
base_url: String,
token_url: String,
timeout: Duration,
user_agent: Option<String>,
}
impl std::fmt::Debug for ClientBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ClientBuilder")
.field("bearer", &self.bearer.as_ref().map(|_| REDACTED))
.field("oauth2", &self.oauth2)
.field("oauth1", &self.oauth1)
.field("hook", &self.hook.is_some())
.field("base_url", &self.base_url)
.field("token_url", &self.token_url)
.field("timeout", &self.timeout)
.field("user_agent", &self.user_agent)
.finish()
}
}
impl ClientBuilder {
pub(crate) fn new() -> Self {
Self {
bearer: None,
oauth2: None,
oauth1: None,
hook: None,
base_url: DEFAULT_API_BASE_URL.to_string(),
token_url: DEFAULT_TOKEN_URL.to_string(),
timeout: Duration::from_secs(DEFAULT_TIMEOUT_SECS),
user_agent: None,
}
}
pub fn bearer(mut self, token: impl Into<String>) -> Self {
self.bearer = Some(token.into());
self
}
pub fn oauth2(mut self, credential: OAuth2Credential) -> Self {
self.oauth2 = Some(credential);
self
}
pub fn oauth1(mut self, credential: OAuth1Credential) -> Self {
self.oauth1 = Some(credential);
self
}
pub fn on_token_refreshed(mut self, hook: impl OnTokenRefreshed) -> Self {
self.hook = Some(Arc::new(hook));
self
}
pub fn base_url(mut self, url: impl Into<String>) -> Self {
self.base_url = url.into();
self
}
pub fn token_url(mut self, url: impl Into<String>) -> Self {
self.token_url = url.into();
self
}
pub fn user_agent(mut self, user_agent: impl Into<String>) -> Self {
self.user_agent = Some(user_agent.into());
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
pub fn build(self) -> Result<Client> {
let credentials = DirectCredentials {
bearer: self.bearer,
oauth1: self.oauth1,
oauth2: self.oauth2,
hook: self.hook,
token_url: self.token_url,
timeout: self.timeout,
};
if credentials.is_empty() {
return Err(Error::validation(
"no credential given: set a bearer token, an OAuth2 credential, or OAuth1 credentials before build()",
));
}
Client::from_source(
self.base_url,
CredentialSource::Direct(credentials),
self.timeout,
self.user_agent
.unwrap_or_else(|| super::DEFAULT_USER_AGENT.to_string()),
)
}
}