use std::sync::Arc;
use std::time::Duration;
use crate::common::http::{HttpClient, DEFAULT_BASE_URL};
use crate::error::{ZtkError, ZtkResult};
const DEFAULT_TIMEOUT_SECS: u64 = 30;
#[derive(Debug, Clone)]
pub struct ZtkClientBuilder {
appkey: String,
base_url: Option<String>,
timeout: Option<Duration>,
}
impl ZtkClientBuilder {
pub fn new(appkey: impl Into<String>) -> Self {
Self {
appkey: appkey.into(),
base_url: None,
timeout: None,
}
}
pub fn base_url(mut self, url: impl Into<String>) -> Self {
self.base_url = Some(url.into());
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn build(self) -> ZtkResult<ZtkClient> {
if self.appkey.is_empty() {
return Err(ZtkError::validation("appkey 不能为空"));
}
let base_url = self
.base_url
.unwrap_or_else(|| DEFAULT_BASE_URL.to_string());
let timeout = self
.timeout
.unwrap_or(Duration::from_secs(DEFAULT_TIMEOUT_SECS));
let http_client = HttpClient::new(&base_url, Some(timeout))?;
Ok(ZtkClient {
inner: Arc::new(ZtkClientInner {
http_client,
base_url,
appkey: self.appkey,
}),
})
}
}
#[derive(Debug)]
struct ZtkClientInner {
http_client: HttpClient,
base_url: String,
appkey: String,
}
#[derive(Debug, Clone)]
pub struct ZtkClient {
inner: Arc<ZtkClientInner>,
}
impl ZtkClient {
pub fn new(appkey: impl Into<String>) -> ZtkClientBuilder {
ZtkClientBuilder::new(appkey)
}
pub fn appkey(&self) -> &str {
&self.inner.appkey
}
pub fn base_url(&self) -> &str {
&self.inner.base_url
}
pub(crate) fn http_client(&self) -> &HttpClient {
&self.inner.http_client
}
#[cfg(feature = "taobao")]
pub fn taobao(&self) -> crate::taobao::TaobaoApi<'_> {
crate::taobao::TaobaoApi::new(self)
}
#[cfg(feature = "jd")]
pub fn jd(&self) -> crate::jd::JdApi<'_> {
crate::jd::JdApi::new(self)
}
#[cfg(feature = "pdd")]
pub fn pdd(&self) -> crate::pdd::PddApi<'_> {
crate::pdd::PddApi::new(self)
}
#[cfg(feature = "vip")]
pub fn vip(&self) -> crate::vip::VipApi<'_> {
crate::vip::VipApi::new(self)
}
#[cfg(feature = "meituan")]
pub fn meituan(&self) -> crate::meituan::MeituanApi<'_> {
crate::meituan::MeituanApi::new(self)
}
#[cfg(feature = "kaola")]
pub fn kaola(&self) -> crate::kaola::KaolaApi<'_> {
crate::kaola::KaolaApi::new(self)
}
#[cfg(feature = "eleme")]
pub fn eleme(&self) -> crate::eleme::ElemeApi<'_> {
crate::eleme::ElemeApi::new(self)
}
#[cfg(feature = "douyin")]
pub fn douyin(&self) -> crate::douyin::DouyinApi<'_> {
crate::douyin::DouyinApi::new(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::http::BACKUP_BASE_URL;
#[test]
fn test_builder_new() {
let builder = ZtkClientBuilder::new("test_appkey");
assert_eq!(builder.appkey, "test_appkey");
assert!(builder.base_url.is_none());
assert!(builder.timeout.is_none());
}
#[test]
fn test_builder_base_url() {
let builder = ZtkClientBuilder::new("test_appkey").base_url("https://custom.api.com");
assert_eq!(builder.base_url, Some("https://custom.api.com".to_string()));
}
#[test]
fn test_builder_timeout() {
let builder = ZtkClientBuilder::new("test_appkey").timeout(Duration::from_secs(60));
assert_eq!(builder.timeout, Some(Duration::from_secs(60)));
}
#[test]
fn test_builder_chain() {
let builder = ZtkClientBuilder::new("test_appkey")
.base_url("https://custom.api.com")
.timeout(Duration::from_secs(60));
assert_eq!(builder.appkey, "test_appkey");
assert_eq!(builder.base_url, Some("https://custom.api.com".to_string()));
assert_eq!(builder.timeout, Some(Duration::from_secs(60)));
}
#[test]
fn test_build_with_defaults() {
let client = ZtkClient::new("test_appkey").build().unwrap();
assert_eq!(client.appkey(), "test_appkey");
assert_eq!(client.base_url(), DEFAULT_BASE_URL);
}
#[test]
fn test_build_with_custom_base_url() {
let client = ZtkClient::new("test_appkey")
.base_url(BACKUP_BASE_URL)
.build()
.unwrap();
assert_eq!(client.appkey(), "test_appkey");
assert_eq!(client.base_url(), BACKUP_BASE_URL);
}
#[test]
fn test_build_empty_appkey_fails() {
let result = ZtkClient::new("").build();
assert!(result.is_err());
match result {
Err(ZtkError::Validation(msg)) => {
assert!(msg.contains("appkey"));
}
_ => panic!("Expected Validation error"),
}
}
#[test]
fn test_client_clone() {
let client = ZtkClient::new("test_appkey").build().unwrap();
let cloned = client.clone();
assert_eq!(client.appkey(), cloned.appkey());
assert_eq!(client.base_url(), cloned.base_url());
}
#[test]
fn test_client_new_returns_builder() {
let builder = ZtkClient::new("test_appkey");
let _client = builder.build().unwrap();
}
}