1use super::{
3 FFIResult, Rid,
4 canvas::ImageRef,
5 error::AidokuError,
6 html::Document,
7 std::{destroy, read_string_and_destroy},
8};
9use crate::alloc::{String, Vec};
10
11#[repr(C)]
13#[derive(PartialEq, Eq, Debug, Clone, Copy)]
14pub enum HttpMethod {
15 Get,
16 Post,
17 Put,
18 Head,
19 Delete,
20 Patch,
21 Options,
22 Connect,
23 Trace,
24}
25
26#[link(wasm_import_module = "net")]
27unsafe extern "C" {
28 fn init(method: HttpMethod) -> Rid;
29 fn send(rid: Rid) -> FFIResult;
30 fn send_all(rd: *mut Rid, len: usize) -> FFIResult;
31
32 fn set_url(rid: Rid, value: *const u8, len: usize) -> FFIResult;
33 fn set_header(
34 rid: Rid,
35 key: *const u8,
36 key_len: usize,
37 val: *const u8,
38 val_len: usize,
39 ) -> FFIResult;
40 fn set_body(rid: Rid, value: *const u8, len: usize) -> FFIResult;
41 fn set_timeout(rid: Rid, value: f64) -> FFIResult;
42
43 fn data_len(rid: Rid) -> FFIResult;
44 fn read_data(rid: Rid, buffer: *mut u8, size: usize) -> FFIResult;
45 fn get_image(rid: Rid) -> FFIResult;
46 fn get_header(rid: Rid, key: *const u8, key_len: usize) -> FFIResult;
47 fn get_status_code(rid: Rid) -> FFIResult;
48 fn get_url(rid: Rid) -> FFIResult;
49 fn html(rid: Rid) -> FFIResult;
50
51 #[link_name = "set_rate_limit"]
52 fn net_set_rate_limit(permits: i32, period: i32, unit: i32);
53}
54
55pub enum TimeUnit {
57 Seconds,
58 Minutes,
59 Hours,
60}
61
62impl From<TimeUnit> for i32 {
63 fn from(unit: TimeUnit) -> i32 {
64 match unit {
65 TimeUnit::Seconds => 0,
66 TimeUnit::Minutes => 1,
67 TimeUnit::Hours => 2,
68 }
69 }
70}
71
72#[derive(PartialEq, Eq, Debug, Clone, Copy)]
74pub enum RequestError {
75 InvalidDescriptor,
76 InvalidString,
77 InvalidMethod,
78 InvalidUrl,
79 InvalidHtml,
80 InvalidBufferSize,
81 MissingData,
82 MissingResponse,
83 MissingUrl,
84 RequestError,
85 FailedMemoryWrite,
86 NotAnImage,
87 Closed,
88}
89
90impl RequestError {
91 fn from(value: i32) -> Option<Self> {
92 match value {
93 -1 => Some(Self::InvalidDescriptor),
94 -2 => Some(Self::InvalidString),
95 -3 => Some(Self::InvalidMethod),
96 -4 => Some(Self::InvalidUrl),
97 -5 => Some(Self::InvalidHtml),
98 -6 => Some(Self::InvalidBufferSize),
99 -7 => Some(Self::MissingData),
100 -8 => Some(Self::MissingResponse),
101 -9 => Some(Self::MissingUrl),
102 -10 => Some(Self::RequestError),
103 -11 => Some(Self::FailedMemoryWrite),
104 -12 => Some(Self::NotAnImage),
105 _ => None,
106 }
107 }
108}
109
110#[doc(hidden)]
113macro_rules! convenience_http_methods {
114 ($name:ident, $t:expr, $doc:tt) => {
115 #[inline]
116 #[doc = $doc]
117 pub fn $name<T: AsRef<str>>(url: T) -> Result<Self, RequestError> {
118 Self::new(url, $t)
119 }
120 };
121}
122
123#[derive(Debug)]
125pub struct Request {
126 pub rid: Rid,
131 http_method: HttpMethod,
132 url: Option<String>,
133 pub should_close: bool,
138}
139
140#[derive(Debug)]
142pub struct Response {
143 rid: Rid,
144 http_method: HttpMethod,
145 url: Option<String>,
146 pub data: Option<Vec<u8>>,
148}
149
150impl Request {
151 pub fn new<T: AsRef<str>>(url: T, http_method: HttpMethod) -> Result<Self, RequestError> {
162 let url = url.as_ref();
163 unsafe {
164 let rid = init(http_method);
165 let mut request = Self {
166 rid,
167 http_method,
168 url: None,
169 should_close: true,
170 };
171 request.set_url(url)?;
172 Ok(request)
173 }
174 }
175
176 convenience_http_methods! { get, HttpMethod::Get, "Create a new GET request with the given URL." }
177 convenience_http_methods! { post, HttpMethod::Post, "Create a new POST request with the given URL." }
178 convenience_http_methods! { put, HttpMethod::Put, "Create a new PUT request with the given URL." }
179 convenience_http_methods! { head, HttpMethod::Head, "Create a new HEAD request with the given URL." }
180 convenience_http_methods! { delete, HttpMethod::Delete, "Create a new DELETE request with the given URL." }
181 convenience_http_methods! { patch, HttpMethod::Patch, "Create a new PATCH request with the given URL." }
182
183 pub fn send_all<I>(requests: I) -> Vec<Result<Response, RequestError>>
185 where
186 I: IntoIterator<Item = Request>,
187 {
188 let mut ids: Vec<i32> = Vec::new();
189 let responses = requests
191 .into_iter()
192 .map(|mut r| {
193 r.should_close = false;
194 ids.push(r.rid);
195 Ok(Response::from(r))
196 })
197 .collect();
198
199 let result = unsafe { send_all(ids.as_mut_ptr(), ids.len()) };
200
201 if result == 0 {
202 responses
204 } else {
205 let mut idx = 0;
208 responses
209 .into_iter()
210 .map(|response| {
211 let error = ids.get(idx).and_then(|id| RequestError::from(*id));
212 let result = match error {
213 Some(e) => Err(e),
214 None => response,
215 };
216 idx += 1;
217 result
218 })
219 .collect()
220 }
221 }
222
223 pub fn header<T: AsRef<str>>(mut self, key: T, val: T) -> Self {
225 self.set_header(key, val);
226 self
227 }
228
229 pub fn set_header<T: AsRef<str>>(&mut self, key: T, val: T) {
231 let key = key.as_ref();
232 let val = val.as_ref();
233 unsafe {
234 set_header(self.rid, key.as_ptr(), key.len(), val.as_ptr(), val.len());
235 };
236 }
237
238 pub fn body<T: AsRef<[u8]>>(mut self, data: T) -> Self {
240 self.set_body(data);
241 self
242 }
243
244 pub fn timeout(mut self, value: f64) -> Self {
249 self.set_timeout(value);
250 self
251 }
252
253 pub fn set_body<T: AsRef<[u8]>>(&mut self, data: T) {
255 let data = data.as_ref();
256 unsafe { set_body(self.rid, data.as_ptr(), data.len()) };
257 }
258
259 pub fn set_timeout(&mut self, value: f64) {
264 unsafe { set_timeout(self.rid, value) };
265 }
266
267 pub fn set_url<T: AsRef<str>>(&mut self, url: T) -> Result<(), RequestError> {
269 let url = url.as_ref();
270 self.url = Some(String::from(url));
271 let result = unsafe { set_url(self.rid, url.as_ptr(), url.len()) };
272 if let Some(error) = RequestError::from(result) {
273 Err(error)
274 } else {
275 Ok(())
276 }
277 }
278
279 pub fn url(&self) -> Option<&String> {
281 self.url.as_ref()
282 }
283
284 #[inline]
286 pub fn send(mut self) -> Result<Response, RequestError> {
287 let result = unsafe { send(self.rid) };
288 if let Some(error) = RequestError::from(result) {
289 Err(error)
290 } else {
291 self.should_close = false;
292 Ok(Response::from(self))
293 }
294 }
295
296 pub fn data(self) -> Result<Vec<u8>, RequestError> {
298 self.send()?.get_data()
299 }
300
301 pub fn image(self) -> Result<ImageRef, RequestError> {
303 self.send()?.get_image()
304 }
305
306 pub fn string(self) -> Result<String, AidokuError> {
308 self.send()?.get_string()
309 }
310
311 pub fn html(self) -> Result<Document, RequestError> {
313 self.send()?.get_html()
314 }
315}
316
317#[cfg(feature = "json")]
318impl Request {
319 pub fn json_owned<T>(self) -> Result<T, AidokuError>
321 where
322 T: serde::de::DeserializeOwned,
323 {
324 self.send()?.get_json_owned()
325 }
326}
327
328impl Response {
329 #[inline]
331 pub fn status_code(&self) -> i32 {
332 unsafe { get_status_code(self.rid) }
333 }
334
335 pub fn get_url(&self) -> Option<String> {
337 let rid = unsafe { get_url(self.rid) };
338 if rid < 0 {
339 return None;
340 }
341 read_string_and_destroy(rid)
342 }
343
344 pub fn get_header<T: AsRef<str>>(&self, header: T) -> Option<String> {
346 let header = header.as_ref();
347 let rid = unsafe { get_header(self.rid, header.as_ptr(), header.len()) };
348 if rid < 0 {
349 return None;
350 }
351 read_string_and_destroy(rid)
352 }
353
354 pub fn get_data(&self) -> Result<Vec<u8>, RequestError> {
356 let size = unsafe { data_len(self.rid) };
357 if let Some(error) = RequestError::from(size) {
358 return Err(error);
359 }
360 let size = size as usize;
361 let mut buffer: Vec<u8> = Vec::with_capacity(size);
362 unsafe {
363 let result = read_data(self.rid, buffer.as_mut_ptr(), size);
364 if let Some(error) = RequestError::from(result) {
365 return Err(error);
366 }
367 buffer.set_len(size);
368 }
369 Ok(buffer)
370 }
371
372 pub fn get_image(&self) -> Result<ImageRef, RequestError> {
374 let result = unsafe { get_image(self.rid) };
375 if let Some(error) = RequestError::from(result) {
376 Err(error)
377 } else {
378 Ok(ImageRef::from(result, false))
379 }
380 }
381
382 pub fn get_string(&self) -> Result<String, AidokuError> {
384 let res = String::from_utf8(self.get_data()?);
385 match res {
386 Ok(res) => Ok(res),
387 Err(err) => Err(AidokuError::Utf8Error(err.utf8_error())),
388 }
389 }
390
391 pub fn get_html(&self) -> Result<Document, RequestError> {
393 let rid = unsafe { html(self.rid) };
394 if let Some(error) = RequestError::from(rid) {
395 return Err(error);
396 }
397 Ok(unsafe { Document::from(rid) })
398 }
399
400 pub fn into_request(self) -> Request {
402 let rid = unsafe { init(self.http_method) };
403 if let Some(url) = self.url.as_ref() {
404 _ = unsafe { set_url(rid, url.as_ptr(), url.len()) };
405 }
406 Request {
407 rid,
408 http_method: self.http_method,
409 url: self.url.clone(),
410 should_close: true,
411 }
412 }
413}
414
415#[cfg(feature = "json")]
416impl Response {
417 pub fn get_json<'a, T>(&'a mut self) -> Result<T, AidokuError>
419 where
420 T: serde::de::Deserialize<'a>,
421 {
422 let data = self.get_data()?;
423 self.data = Some(data);
424 let value = serde_json::from_slice(self.data.as_ref().unwrap())?;
425 Ok(value)
426 }
427
428 pub fn get_json_owned<T>(self) -> Result<T, AidokuError>
430 where
431 T: serde::de::DeserializeOwned,
432 {
433 let data = self.get_data()?;
434 let value = serde_json::from_slice(&data)?;
435 Ok(value)
436 }
437}
438
439impl Response {
440 fn from(request: Request) -> Self {
442 Self {
443 rid: request.rid,
444 http_method: request.http_method,
445 url: request.url.clone(),
446 data: None,
447 }
448 }
449}
450
451impl Drop for Request {
452 fn drop(&mut self) {
453 if self.should_close {
454 unsafe { destroy(self.rid) };
455 }
456 }
457}
458
459impl Drop for Response {
460 fn drop(&mut self) {
461 unsafe { destroy(self.rid) };
462 }
463}
464
465pub fn set_rate_limit(permits: i32, period: i32, unit: TimeUnit) {
471 unsafe { net_set_rate_limit(permits, period, unit.into()) }
472}