use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use crate::error::Error;
use crate::http::HttpClient;
use crate::resources::{
AutomationsResource, ContactsResource, EmailsResource, SegmentsResource, TopicsResource,
};
const DEFAULT_BASE_URL: &str = "https://parcelwing.com";
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
#[derive(Clone, Debug)]
pub struct Client {
http: Arc<HttpClient>,
}
#[derive(Clone, Debug)]
pub struct ClientOptions {
pub api_key: String,
pub base_url: Option<String>,
pub timeout: Option<Duration>,
pub default_headers: HashMap<String, String>,
}
impl Client {
pub fn new(api_key: impl Into<String>) -> Result<Self, Error> {
Self::builder(api_key).build()
}
pub fn with_options(options: ClientOptions) -> Result<Self, Error> {
let mut builder = Self::builder(options.api_key);
if let Some(base_url) = options.base_url {
builder = builder.base_url(base_url);
}
if let Some(timeout) = options.timeout {
builder = builder.timeout(timeout);
}
for (key, value) in options.default_headers {
builder = builder.default_header(key, value);
}
builder.build()
}
pub fn builder(api_key: impl Into<String>) -> ClientBuilder {
ClientBuilder::new(api_key)
}
pub fn emails(&self) -> EmailsResource {
EmailsResource::new(self.http.clone())
}
pub fn contacts(&self) -> ContactsResource {
ContactsResource::new(self.http.clone())
}
pub fn segments(&self) -> SegmentsResource {
SegmentsResource::new(self.http.clone())
}
pub fn topics(&self) -> TopicsResource {
TopicsResource::new(self.http.clone())
}
pub fn automations(&self) -> AutomationsResource {
AutomationsResource::new(self.http.clone())
}
}
#[derive(Debug)]
pub struct ClientBuilder {
api_key: String,
base_url: String,
timeout: Duration,
default_headers: HashMap<String, String>,
http_client: Option<reqwest::Client>,
}
impl ClientBuilder {
pub fn new(api_key: impl Into<String>) -> Self {
Self {
api_key: api_key.into(),
base_url: DEFAULT_BASE_URL.to_string(),
timeout: DEFAULT_TIMEOUT,
default_headers: HashMap::new(),
http_client: None,
}
}
pub fn base_url(mut self, base_url: impl Into<String>) -> Self {
self.base_url = base_url.into();
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
pub fn default_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.default_headers.insert(key.into(), value.into());
self
}
pub fn http_client(mut self, http_client: reqwest::Client) -> Self {
self.http_client = Some(http_client);
self
}
pub fn build(self) -> Result<Client, Error> {
let api_key = self.api_key.trim().to_string();
if api_key.is_empty() {
return Err(Error::Configuration(
"Parcel Wing client requires a non-empty API key.".to_string(),
));
}
let base_url = self.base_url.trim().trim_end_matches('/').to_string();
if base_url.is_empty() {
return Err(Error::Configuration(
"Parcel Wing client requires a non-empty base URL.".to_string(),
));
}
let http_client = match self.http_client {
Some(client) => client,
None => reqwest::Client::builder()
.timeout(self.timeout)
.build()
.map_err(Error::HttpClientBuild)?,
};
Ok(Client {
http: Arc::new(HttpClient::new(
api_key,
base_url,
http_client,
self.default_headers,
)),
})
}
}