use std::time::Duration;
use just_common::error::TransportError;
use just_common::transport::http;
use crate::{DeepSeekClient, Error};
const DEFAULT_BASE_URL: &str = "https://api.deepseek.com";
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60);
pub struct DeepSeekClientBuilder {
api_key: Option<String>,
base_url: Option<String>,
http_builder: Option<reqwest::ClientBuilder>,
}
impl DeepSeekClientBuilder {
pub(crate) fn new() -> Self {
Self {
api_key: None,
base_url: None,
http_builder: None,
}
}
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: impl Into<String>) -> Self {
self.base_url = Some(url.into());
self
}
pub fn http_client(mut self, builder: reqwest::ClientBuilder) -> Self {
self.http_builder = Some(builder);
self
}
pub fn build(self) -> Result<DeepSeekClient, Error> {
let api_key = self.api_key.ok_or_else(|| {
Error::Transport(TransportError::InvalidConfig("api key is required"))
})?;
if api_key.trim().is_empty() {
return Err(Error::Transport(TransportError::InvalidConfig(
"api key cannot be empty",
)));
}
let base_url = self.base_url.unwrap_or_else(|| DEFAULT_BASE_URL.to_owned());
if base_url.trim().is_empty() {
return Err(Error::Transport(TransportError::InvalidConfig(
"base url cannot be empty",
)));
}
let builder = self.http_builder.unwrap_or_else(|| {
reqwest::Client::builder()
.timeout(DEFAULT_TIMEOUT)
.use_rustls_tls()
});
let http = http::build_client(builder, &api_key)?;
Ok(DeepSeekClient::new(http, base_url))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_missing_api_key() {
let error = DeepSeekClient::builder().build().unwrap_err();
assert!(matches!(
error,
Error::Transport(TransportError::InvalidConfig("api key is required"))
));
}
#[test]
fn rejects_empty_api_key() {
let error = DeepSeekClient::builder()
.api_key(" ")
.build()
.unwrap_err();
assert!(matches!(
error,
Error::Transport(TransportError::InvalidConfig("api key cannot be empty"))
));
}
}