1use crate::error::{CufinderError, Result};
2use reqwest::{Client as ReqwestClient, header};
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 mut headers = header::HeaderMap::new();
37 headers.insert(
38 header::AUTHORIZATION,
39 header::HeaderValue::from_str(&format!("Bearer {}", config.api_key))
40 .map_err(|e| CufinderError::ValidationError(format!("Invalid API key: {}", e)))?,
41 );
42 headers.insert(
43 header::CONTENT_TYPE,
44 header::HeaderValue::from_static("application/json"),
45 );
46
47 let http_client = ReqwestClient::builder()
48 .timeout(config.timeout)
49 .default_headers(headers)
50 .build()
51 .map_err(CufinderError::HttpError)?;
52
53 Ok(Self {
54 config,
55 http_client,
56 })
57 }
58
59 pub fn with_api_key(api_key: String) -> Result<Self> {
61 Self::new(ClientConfig {
62 api_key,
63 ..Default::default()
64 })
65 }
66
67 pub async fn post<T>(&self, endpoint: &str, data: &T) -> Result<serde_json::Value>
69 where
70 T: Serialize,
71 {
72 let url = format!("{}{}", self.config.base_url, endpoint);
73
74 let response = self
75 .http_client
76 .post(&url)
77 .json(data)
78 .send()
79 .await
80 .map_err(CufinderError::HttpError)?;
81
82 let status = response.status();
83
84 if !status.is_success() {
85 let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
86
87 return match status.as_u16() {
88 401 => Err(CufinderError::AuthenticationError(error_text)),
89 429 => Err(CufinderError::RateLimitError(error_text)),
90 402 => Err(CufinderError::CreditLimitError(error_text)),
91 _ => Err(CufinderError::ApiError {
92 status: status.as_u16(),
93 message: error_text,
94 }),
95 };
96 }
97
98 let json_response: serde_json::Value = response
99 .json()
100 .await
101 .map_err(CufinderError::HttpError)?;
102
103 Ok(json_response)
104 }
105
106 pub fn http_client(&self) -> &ReqwestClient {
108 &self.http_client
109 }
110
111 pub fn config(&self) -> &ClientConfig {
113 &self.config
114 }
115}