cufinder_rust/
client.rs

1use crate::error::{CufinderError, Result};
2use reqwest::{Client as ReqwestClient, header};
3use serde::Serialize;
4use std::time::Duration;
5
6/// Configuration for the CUFinder client
7#[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/// HTTP client for CUFinder API
27#[derive(Debug, Clone)]
28pub struct Client {
29    config: ClientConfig,
30    http_client: ReqwestClient,
31}
32
33impl Client {
34    /// Create a new client with the given configuration
35    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    /// Create a new client with just an API key
60    pub fn with_api_key(api_key: String) -> Result<Self> {
61        Self::new(ClientConfig {
62            api_key,
63            ..Default::default()
64        })
65    }
66
67    /// Send a POST request to the API
68    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    /// Get the underlying HTTP client for advanced usage
107    pub fn http_client(&self) -> &ReqwestClient {
108        &self.http_client
109    }
110
111    /// Get the client configuration
112    pub fn config(&self) -> &ClientConfig {
113        &self.config
114    }
115}