1use crate::error::{CufinderError, Result};
2use reqwest::Client as ReqwestClient;
3use serde::Serialize;
4use std::time::Duration;
5
6#[derive(Debug, Clone)]
8pub struct ClientConfig {
9 pub api_key: String,
10 pub base_url: String,
11 pub timeout: Duration,
12 pub max_retries: u32,
13}
14
15impl Default for ClientConfig {
16 fn default() -> Self {
17 Self {
18 api_key: String::new(),
19 base_url: "https://api.cufinder.io/v2".to_string(),
20 timeout: Duration::from_secs(30),
21 max_retries: 3,
22 }
23 }
24}
25
26#[derive(Debug, Clone)]
28pub struct Client {
29 config: ClientConfig,
30 http_client: ReqwestClient,
31}
32
33impl Client {
34 pub fn new(config: ClientConfig) -> Result<Self> {
36 let http_client = ReqwestClient::builder()
37 .timeout(config.timeout)
38 .build()
39 .map_err(CufinderError::HttpError)?;
40
41 Ok(Self {
42 config,
43 http_client,
44 })
45 }
46
47 pub fn with_api_key(api_key: String) -> Result<Self> {
49 Self::new(ClientConfig {
50 api_key,
51 ..Default::default()
52 })
53 }
54
55 pub async fn post<T>(&self, endpoint: &str, data: &T) -> Result<serde_json::Value>
57 where
58 T: Serialize,
59 {
60 let url = format!("{}{}", self.config.base_url, endpoint);
61
62 let form_data = serde_urlencoded::to_string(data)
64 .map_err(|e| CufinderError::ValidationError(format!("Failed to encode form data: {}", e)))?;
65
66 let response = self
67 .http_client
68 .post(&url)
69 .header("x-api-key", &self.config.api_key)
70 .header("Content-Type", "application/x-www-form-urlencoded")
71 .header("User-Agent", "cufinder-rust/1.0.0")
72 .body(form_data)
73 .send()
74 .await
75 .map_err(CufinderError::HttpError)?;
76
77 let status = response.status();
78
79 if !status.is_success() {
80 let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
81
82 return match status.as_u16() {
83 401 => Err(CufinderError::AuthenticationError(error_text)),
84 429 => Err(CufinderError::RateLimitError(error_text)),
85 402 => Err(CufinderError::CreditLimitError(error_text)),
86 _ => Err(CufinderError::ApiError {
87 status: status.as_u16(),
88 message: error_text,
89 }),
90 };
91 }
92
93 let mut json_response: serde_json::Value = response
94 .json()
95 .await
96 .map_err(CufinderError::HttpError)?;
97
98 if let Some(data_wrapper) = json_response.get("data") {
100 if let Some(meta_data) = json_response.get("meta_data") {
101 let mut data_obj = data_wrapper.clone();
103 if let serde_json::Value::Object(ref mut map) = data_obj {
104 map.insert("meta_data".to_string(), meta_data.clone());
105 }
106 json_response = data_obj;
107 } else {
108 json_response = data_wrapper.clone();
109 }
110 }
111
112 Ok(json_response)
113 }
114
115 pub fn http_client(&self) -> &ReqwestClient {
117 &self.http_client
118 }
119
120 pub fn config(&self) -> &ClientConfig {
122 &self.config
123 }
124}