use std::time::Duration;
use crate::error::Error;
pub const SDK_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const SDK_LANGUAGE: &str = "rust";
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
pub const DEFAULT_MAX_RETRIES: u32 = 3;
#[derive(Debug, Clone)]
pub enum AuthConfig {
Bearer(String),
Cookie,
}
#[derive(Debug, Clone)]
pub struct ClientConfig {
pub base_url: String,
pub tenant_id: String,
pub auth: AuthConfig,
pub user_id: Option<String>,
pub namespace_id: Option<String>,
pub authenticated_user: Option<String>,
pub timeout: Duration,
pub max_retries: u32,
pub retry_on_503: bool,
pub extra_headers: Vec<(String, String)>,
}
impl ClientConfig {
pub fn new(
base_url: impl Into<String>,
tenant_id: impl Into<String>,
auth: AuthConfig,
) -> Self {
Self {
base_url: base_url.into(),
tenant_id: tenant_id.into(),
auth,
user_id: None,
namespace_id: None,
authenticated_user: None,
timeout: DEFAULT_TIMEOUT,
max_retries: DEFAULT_MAX_RETRIES,
retry_on_503: false,
extra_headers: Vec::new(),
}
}
#[must_use]
pub fn with_user_id(mut self, user_id: impl Into<String>) -> Self {
self.user_id = Some(user_id.into());
self
}
#[must_use]
pub fn with_namespace_id(mut self, namespace_id: impl Into<String>) -> Self {
self.namespace_id = Some(namespace_id.into());
self
}
#[must_use]
pub fn with_authenticated_user(mut self, authenticated_user: impl Into<String>) -> Self {
self.authenticated_user = Some(authenticated_user.into());
self
}
#[must_use]
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
#[must_use]
pub fn with_max_retries(mut self, max_retries: u32) -> Self {
self.max_retries = max_retries;
self
}
#[must_use]
pub fn with_retry_on_503(mut self, retry_on_503: bool) -> Self {
self.retry_on_503 = retry_on_503;
self
}
#[must_use]
pub fn with_header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
self.extra_headers.push((name.into(), value.into()));
self
}
pub(crate) fn resolve(self) -> Result<ResolvedConfig, Error> {
if self.base_url.is_empty() {
return Err(Error::validation("base_url", "base_url is required"));
}
if self.tenant_id.is_empty() {
return Err(Error::validation("tenant_id", "tenant_id is required"));
}
if let AuthConfig::Bearer(token) = &self.auth {
if token.is_empty() {
return Err(Error::validation(
"auth",
"AuthConfig::Bearer requires a non-empty token",
));
}
}
let base_url = self.base_url.trim_end_matches('/').to_string();
Ok(ResolvedConfig {
base_url,
tenant_id: self.tenant_id,
auth: self.auth,
user_id: self.user_id,
namespace_id: self.namespace_id,
authenticated_user: self.authenticated_user,
timeout: self.timeout,
max_retries: self.max_retries,
retry_on_503: self.retry_on_503,
extra_headers: self.extra_headers,
})
}
}
#[derive(Debug, Clone)]
pub(crate) struct ResolvedConfig {
pub base_url: String,
pub tenant_id: String,
pub auth: AuthConfig,
pub user_id: Option<String>,
pub namespace_id: Option<String>,
pub authenticated_user: Option<String>,
pub timeout: Duration,
pub max_retries: u32,
pub retry_on_503: bool,
pub extra_headers: Vec<(String, String)>,
}