auto_api_client/
client.rs1use std::collections::HashMap;
2use std::time::Duration;
3
4use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
5use serde_json::Value;
6
7use crate::error::Error;
8use crate::types::*;
9
10pub struct Client {
12 api_key: String,
13 base_url: String,
14 api_version: String,
15 http_client: reqwest::Client,
16}
17
18impl Client {
19 pub fn new(api_key: &str) -> Self {
21 Self {
22 api_key: api_key.to_string(),
23 base_url: "https://auto-api.com".to_string(),
24 api_version: "v2".to_string(),
25 http_client: reqwest::Client::builder()
26 .timeout(Duration::from_secs(30))
27 .build()
28 .expect("failed to build HTTP client"),
29 }
30 }
31
32 pub fn set_base_url(&mut self, base_url: &str) {
34 self.base_url = base_url.trim_end_matches('/').to_string();
35 }
36
37 pub fn set_api_version(&mut self, version: &str) {
39 self.api_version = version.to_string();
40 }
41
42 pub async fn get_filters(&self, source: &str) -> Result<Value, Error> {
44 let url = format!(
45 "{}/api/{}/{}/filters",
46 self.base_url, self.api_version, source
47 );
48 self.get(&url, &[]).await
49 }
50
51 pub async fn get_offers(
53 &self,
54 source: &str,
55 params: &OffersParams,
56 ) -> Result<OffersResponse, Error> {
57 let url = format!(
58 "{}/api/{}/{}/offers",
59 self.base_url, self.api_version, source
60 );
61 let pairs = params.to_query_pairs();
62 let query: Vec<(&str, &str)> = pairs.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
63 self.get(&url, &query).await
64 }
65
66 pub async fn get_offer(
68 &self,
69 source: &str,
70 inner_id: &str,
71 ) -> Result<OffersResponse, Error> {
72 let url = format!(
73 "{}/api/{}/{}/offer",
74 self.base_url, self.api_version, source
75 );
76 self.get(&url, &[("inner_id", inner_id)]).await
77 }
78
79 pub async fn get_change_id(&self, source: &str, date: &str) -> Result<i64, Error> {
81 let url = format!(
82 "{}/api/{}/{}/change_id",
83 self.base_url, self.api_version, source
84 );
85 let result: ChangeIdResponse = self.get(&url, &[("date", date)]).await?;
86 Ok(result.change_id)
87 }
88
89 pub async fn get_changes(
91 &self,
92 source: &str,
93 change_id: i64,
94 ) -> Result<ChangesResponse, Error> {
95 let url = format!(
96 "{}/api/{}/{}/changes",
97 self.base_url, self.api_version, source
98 );
99 let change_id_str = change_id.to_string();
100 self.get(&url, &[("change_id", &change_id_str)]).await
101 }
102
103 pub async fn get_offer_by_url(&self, offer_url: &str) -> Result<Value, Error> {
106 let url = format!("{}/api/v1/offer/info", self.base_url);
107
108 let mut body = HashMap::new();
109 body.insert("url", offer_url);
110
111 let mut headers = HeaderMap::new();
112 headers.insert("x-api-key", HeaderValue::from_str(&self.api_key).unwrap());
113 headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
114
115 let response = self
116 .http_client
117 .post(&url)
118 .headers(headers)
119 .json(&body)
120 .send()
121 .await?;
122
123 self.handle_response(response).await
124 }
125
126 async fn get<T: serde::de::DeserializeOwned>(
127 &self,
128 url: &str,
129 query: &[(&str, &str)],
130 ) -> Result<T, Error> {
131 let mut all_query: Vec<(&str, &str)> = query.to_vec();
132 all_query.push(("api_key", &self.api_key));
133
134 let response = self
135 .http_client
136 .get(url)
137 .query(&all_query)
138 .send()
139 .await?;
140
141 self.handle_response(response).await
142 }
143
144 async fn handle_response<T: serde::de::DeserializeOwned>(
145 &self,
146 response: reqwest::Response,
147 ) -> Result<T, Error> {
148 let status = response.status().as_u16();
149 let body = response.text().await?;
150
151 if status < 200 || status >= 300 {
152 let mut message = format!("API error: {}", status);
153
154 if let Ok(parsed) = serde_json::from_str::<Value>(&body) {
155 if let Some(msg) = parsed.get("message").and_then(|m| m.as_str()) {
156 message = msg.to_string();
157 }
158 }
159
160 if status == 401 || status == 403 {
161 return Err(Error::Auth {
162 status_code: status,
163 message,
164 });
165 }
166
167 return Err(Error::Api {
168 status_code: status,
169 message,
170 body,
171 });
172 }
173
174 serde_json::from_str(&body).map_err(|_| Error::Api {
175 status_code: status,
176 message: format!(
177 "Invalid JSON response: {}",
178 &body[..body.len().min(200)]
179 ),
180 body,
181 })
182 }
183}