1use crate::http::types::*;
5use crate::http::error::*;
6
7pub mod types;
8pub mod error;
9pub mod paginator;
10
11async fn read_http_response<T>(response: reqwest::Response) -> HttpResult<T>
14where
15 T: serde::de::DeserializeOwned
16{
17 let status = response.status();
19 if !status.is_success() {
20 let error_text = response.text().await.unwrap_or_else(|_| "Unknown error!".to_string());
21 return Err(HttpError::HttpStatus {
22 status: status.as_u16(),
23 message: error_text
24 })
25 }
26
27 let json: serde_json::Value = response.json().await?;
29 let success = json.get("success")
30 .and_then(|v| v.as_bool())
31 .unwrap_or(false);
32
33 if !success {
34 let message = json.get("error_message")
35 .and_then(|v| v.as_str())
36 .unwrap_or("Unknown API error!")
37 .to_string();
38 return Err(HttpError::ApiError { message })
39 }
40
41 let response_value = json.get("response")
43 .ok_or(HttpError::MissingResponseField)?;
44
45 serde_json::from_value(response_value.clone())
46 .map_err(HttpError::JsonError)
47}
48
49async fn read_modem_response<T>(expected: &str, response: reqwest::Response) -> HttpResult<T>
52where
53 T: serde::de::DeserializeOwned
54{
55
56 let json_response: serde_json::Value = read_http_response(response).await?;
58 let actual = json_response.get("type")
59 .and_then(|v| v.as_str())
60 .ok_or(HttpError::MissingTypeField)?;
61
62 if actual != expected {
63 return Err(HttpError::ResponseTypeMismatch {
64 expected: expected.to_string(),
65 actual: actual.to_string()
66 });
67 }
68
69 let data = json_response.get("data")
71 .ok_or(HttpError::MissingDataField)?;
72
73 serde_json::from_value(data.clone())
74 .map_err(HttpError::JsonError)
75}
76
77#[derive(Debug)]
79pub struct HttpClient {
80 base_url: reqwest::Url,
81 authorization: Option<String>,
82 modem_timeout: Option<std::time::Duration>,
83 client: reqwest::Client
84}
85impl HttpClient {
86
87 pub fn new(config: crate::config::HttpConfig) -> HttpResult<Self> {
89 let client = reqwest::Client::builder()
90 .timeout(config.base_timeout)
91 .build()?;
92
93 Ok(Self {
94 base_url: reqwest::Url::parse(config.url.as_str())?,
95 authorization: config.authorization.map(|a| a.into()),
96 modem_timeout: config.modem_timeout,
97 client
98 })
99 }
100
101 pub async fn set_friendly_name(&self, phone_number: impl Into<String>, friendly_name: Option<impl Into<String>>) -> HttpResult<bool> {
103 let body = serde_json::json!({
104 "phone_number": phone_number.into(),
105 "friendly_name": friendly_name.map(Into::into)
106 });
107
108 let url = self.base_url.join("/db/friendly-names/set")?;
109 let response = self.setup_request(false, self.client.post(url))
110 .json(&body)
111 .send()
112 .await?;
113
114 read_http_response(response).await
115 }
116
117 pub async fn get_friendly_name(&self, phone_number: impl Into<String>) -> HttpResult<Option<String>> {
119 let body = serde_json::json!({
120 "phone_number": phone_number.into()
121 });
122
123 let url = self.base_url.join("/db/friendly-names/get")?;
124 let response = self.setup_request(false, self.client.post(url))
125 .json(&body)
126 .send()
127 .await?;
128
129 read_http_response(response).await
130 }
131
132 pub async fn get_messages(&self, phone_number: impl Into<String>, pagination: Option<HttpPaginationOptions>) -> HttpResult<Vec<crate::types::SmsStoredMessage>> {
135 let mut body = serde_json::json!({
136 "phone_number": phone_number.into()
137 });
138 if let Some(pagination) = pagination {
139 pagination.add_to_body(&mut body);
140 }
141
142 let url = self.base_url.join("/db/sms")?;
143 let response = self.setup_request(false, self.client.post(url))
144 .json(&body)
145 .send()
146 .await?;
147
148 read_http_response(response).await
149 }
150
151 pub async fn get_latest_numbers(&self, pagination: Option<HttpPaginationOptions>) -> HttpResult<Vec<LatestNumberFriendlyNamePair>> {
154 let url = self.base_url.join("/db/latest-numbers")?;
155 let mut request = self.setup_request(false, self.client.post(url));
156
157 if let Some(pagination) = pagination {
159 request = request.json(&pagination);
160 }
161
162 let response = request.send().await?;
163 read_http_response(response).await
164 }
165
166 pub async fn get_delivery_reports(&self, message_id: i64, pagination: Option<HttpPaginationOptions>) -> HttpResult<Vec<HttpSmsDeliveryReport>> {
169 let mut body = serde_json::json!({
170 "message_id": message_id
171 });
172 if let Some(pagination) = pagination {
173 pagination.add_to_body(&mut body);
174 }
175
176 let url = self.base_url.join("/db/delivery-reports")?;
177 let response = self.setup_request(false, self.client.post(url))
178 .json(&body)
179 .send()
180 .await?;
181
182 read_http_response(response).await
183 }
184
185 pub async fn send_sms(&self, message: &HttpOutgoingSmsMessage) -> HttpResult<HttpSmsSendResponse> {
189 let url = self.base_url.join("/sms/send")?;
190
191 let mut request = self.setup_request(true, self.client.post(url));
194 if let Some(timeout) = message.timeout {
195 request = request.timeout(std::time::Duration::from_secs(timeout as u64 + 5));
196 }
197
198 let response = request.json(message)
199 .send()
200 .await?;
201
202 read_http_response(response).await
203 }
204
205 pub async fn get_network_status(&self) -> HttpResult<HttpModemNetworkStatusResponse> {
207 self.modem_request("modem-status", "NetworkStatus").await
208 }
209
210 pub async fn get_signal_strength(&self) -> HttpResult<HttpModemSignalStrengthResponse> {
212 self.modem_request("signal-strength", "SignalStrength").await
213 }
214
215 pub async fn get_network_operator(&self) -> HttpResult<HttpModemNetworkOperatorResponse> {
218 self.modem_request("network-operator", "NetworkOperator").await
219 }
220
221 pub async fn get_service_provider(&self) -> HttpResult<String> {
224 self.modem_request("service-provider", "ServiceProvider").await
225 }
226
227 pub async fn get_battery_level(&self) -> HttpResult<HttpModemBatteryLevelResponse> {
229 self.modem_request("battery-level", "BatteryLevel").await
230 }
231
232 pub async fn get_device_info(&self) -> HttpResult<HttpSmsDeviceInfoData> {
234 let url = self.base_url.join("/sms/device-info")?;
235 let response = self.setup_request(true, self.client.get(url))
236 .send()
237 .await?;
238
239 let response = read_http_response::<HttpSmsDeviceInfoResponse>(response).await?;
240 Ok(HttpSmsDeviceInfoData::from(response))
241 }
242
243 pub async fn get_phone_number(&self) -> HttpResult<Option<String>> {
246 let url = self.base_url.join("/sys/phone-number")?;
247 let response = self.setup_request(false, self.client.get(url))
248 .send()
249 .await?;
250
251 read_http_response(response).await
252 }
253
254 pub async fn get_version(&self) -> HttpResult<String> {
257 let url = self.base_url.join("/sys/version")?;
258 let response = self.setup_request(false, self.client.get(url))
259 .send()
260 .await?;
261
262 read_http_response(response).await
263 }
264
265 async fn modem_request<T>(&self, route: &str, expected: &str) -> HttpResult<T>
267 where
268 T: serde::de::DeserializeOwned
269 {
270 let url = self.base_url.join(&format!("/sms/{route}"))?;
271 let response = self.setup_request(true, self.client.get(url))
272 .send()
273 .await?;
274
275 read_modem_response::<T>(expected, response).await
276 }
277
278 fn setup_request(&self, is_modem: bool, builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
281 let builder = if is_modem && let Some(timeout) = &self.modem_timeout {
282 builder.timeout(*timeout)
283 } else {
284 builder
285 };
286 if let Some(auth) = &self.authorization {
287 builder.header("authorization", auth)
288 } else {
289 builder
290 }
291 }
292}