use std::sync::Arc;
use std::time::Duration;
use url::Url;
use crate::auth::Credentials;
use crate::config::{DEFAULT_BASE_URL, ENV_API_KEY, ENV_BASE_URL};
use crate::error::{Error, Result};
use crate::messages::MessagesService;
#[derive(Debug, Clone)]
pub struct Client {
pub(crate) inner: Arc<ClientInner>,
}
#[derive(Debug)]
pub(crate) struct ClientInner {
pub(crate) http: reqwest::Client,
pub(crate) base_url: Url,
pub(crate) credentials: Credentials,
}
impl Client {
pub fn from_env() -> Result<Self> {
let api_key = std::env::var(ENV_API_KEY)
.map_err(|_| Error::Config(format!("environment variable {ENV_API_KEY} is not set")))?;
let mut builder = Self::builder().api_key(api_key);
if let Ok(url) = std::env::var(ENV_BASE_URL) {
builder = builder.base_url(
Url::parse(&url)
.map_err(|e| Error::Config(format!("invalid {ENV_BASE_URL}: {e}")))?,
);
}
builder.build()
}
pub fn builder() -> ClientBuilder {
ClientBuilder::default()
}
pub fn messages(&self) -> MessagesService<'_> {
MessagesService::new(self)
}
}
#[derive(Debug, Default)]
pub struct ClientBuilder {
api_key: Option<String>,
base_url: Option<Url>,
timeout: Option<Duration>,
http_client: Option<reqwest::Client>,
}
impl ClientBuilder {
pub fn api_key(mut self, key: impl Into<String>) -> Self {
self.api_key = Some(key.into());
self
}
pub fn base_url(mut self, url: Url) -> Self {
self.base_url = Some(url);
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn http_client(mut self, http: reqwest::Client) -> Self {
self.http_client = Some(http);
self
}
pub fn build(self) -> Result<Client> {
let api_key = self
.api_key
.ok_or_else(|| Error::Config("api_key is required".into()))?;
if api_key.trim().is_empty() {
return Err(Error::Config("api_key must not be empty".into()));
}
let base_url = match self.base_url {
Some(u) => u,
None => Url::parse(DEFAULT_BASE_URL)
.map_err(|e| Error::Config(format!("default base URL is invalid: {e}")))?,
};
let http = match self.http_client {
Some(c) => c,
None => {
let mut b = reqwest::Client::builder();
if let Some(t) = self.timeout {
b = b.timeout(t);
}
b.build().map_err(Error::Http)?
}
};
Ok(Client {
inner: Arc::new(ClientInner {
http,
base_url,
credentials: Credentials::from_api_key(api_key),
}),
})
}
}