use std::time::Duration;
use reqwest::header::HeaderValue;
use reqwest::header::IntoHeaderName;
use url::Url;
use crate::v1::client::Client;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("missing required field `{0}`")]
Missing(&'static str),
#[error(transparent)]
Reqwest(#[from] reqwest::Error),
#[error(transparent)]
Url(#[from] url::ParseError),
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Clone, Debug, Default)]
pub struct Builder {
url: Option<Url>,
headers: reqwest::header::HeaderMap,
connect_timeout: Option<Duration>,
read_timeout: Option<Duration>,
}
impl Builder {
const DEFAULT_CONNECT_TIMEOUT: Duration = Duration::from_secs(60);
const DEFAULT_READ_TIMEOUT: Duration = Duration::from_secs(60);
pub fn url(mut self, url: impl Into<Url>) -> Self {
let mut url = url.into();
if !url.path().ends_with("/") {
url.set_path(&format!("{}/", url.path()));
}
self.url = Some(url);
self
}
pub fn url_from_string(self, url: impl AsRef<str>) -> Result<Self> {
let url = url.as_ref().parse::<Url>()?;
Ok(self.url(url))
}
pub fn insert_header<K>(mut self, key: K, value: impl AsRef<str>) -> Self
where
K: IntoHeaderName,
{
let value = value.as_ref();
self.headers.insert::<K>(
key,
HeaderValue::from_str(value)
.unwrap_or_else(|_| panic!("value for header is not allowed: {value}")),
);
self
}
pub fn connect_timeout(mut self, timeout: Duration) -> Self {
self.connect_timeout = Some(timeout);
self
}
pub fn read_timeout(mut self, timeout: Duration) -> Self {
self.read_timeout = Some(timeout);
self
}
pub fn try_build(self) -> Result<Client> {
let url = self.url.map(Ok).unwrap_or(Err(Error::Missing("url")))?;
let client = reqwest::ClientBuilder::new()
.connect_timeout(
self.connect_timeout
.unwrap_or(Self::DEFAULT_CONNECT_TIMEOUT),
)
.read_timeout(self.read_timeout.unwrap_or(Self::DEFAULT_READ_TIMEOUT))
.default_headers(self.headers)
.build()?;
Ok(Client { url, client })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_adds_a_trailing_slash() {
let client = Builder::default()
.url_from_string("http://localhost:4000/v1")
.unwrap()
.try_build()
.unwrap();
assert_eq!(client.url.as_str(), "http://localhost:4000/v1/");
}
}