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 html(rid: Rid) -> FFIResult;
49
50 #[link_name = "set_rate_limit"]
51 fn net_set_rate_limit(permits: i32, period: i32, unit: i32);
52}
53
54pub enum TimeUnit {
56 Seconds,
57 Minutes,
58 Hours,
59}
60
61impl From<TimeUnit> for i32 {
62 fn from(unit: TimeUnit) -> i32 {
63 match unit {
64 TimeUnit::Seconds => 0,
65 TimeUnit::Minutes => 1,
66 TimeUnit::Hours => 2,
67 }
68 }
69}
70
71#[derive(PartialEq, Eq, Debug, Clone, Copy)]
73pub enum RequestError {
74 InvalidDescriptor,
75 InvalidString,
76 InvalidMethod,
77 InvalidUrl,
78 InvalidHtml,
79 InvalidBufferSize,
80 MissingData,
81 MissingResponse,
82 MissingUrl,
83 RequestError,
84 FailedMemoryWrite,
85 NotAnImage,
86 Closed,
87}
88
89impl RequestError {
90 fn from(value: i32) -> Option<Self> {
91 match value {
92 -1 => Some(Self::InvalidDescriptor),
93 -2 => Some(Self::InvalidString),
94 -3 => Some(Self::InvalidMethod),
95 -4 => Some(Self::InvalidUrl),
96 -5 => Some(Self::InvalidHtml),
97 -6 => Some(Self::InvalidBufferSize),
98 -7 => Some(Self::MissingData),
99 -8 => Some(Self::MissingResponse),
100 -9 => Some(Self::MissingUrl),
101 -10 => Some(Self::RequestError),
102 -11 => Some(Self::FailedMemoryWrite),
103 -12 => Some(Self::NotAnImage),
104 _ => None,
105 }
106 }
107}
108
109#[doc(hidden)]
112macro_rules! convenience_http_methods {
113 ($name:ident, $t:expr, $doc:tt) => {
114 #[inline]
115 #[doc = $doc]
116 pub fn $name<T: AsRef<str>>(url: T) -> Result<Self, RequestError> {
117 Self::new(url, $t)
118 }
119 };
120}
121
122#[derive(Debug)]
124pub struct Request {
125 pub rid: Rid,
130 http_method: HttpMethod,
131 url: Option<String>,
132 pub should_close: bool,
137}
138
139#[derive(Debug)]
141pub struct Response {
142 rid: Rid,
143 http_method: HttpMethod,
144 url: Option<String>,
145 pub data: Option<Vec<u8>>,
147}
148
149impl Request {
150 pub fn new<T: AsRef<str>>(url: T, http_method: HttpMethod) -> Result<Self, RequestError> {
161 let url = url.as_ref();
162 unsafe {
163 let rid = init(http_method);
164 let mut request = Self {
165 rid,
166 http_method,
167 url: None,
168 should_close: true,
169 };
170 request.set_url(url)?;
171 Ok(request)
172 }
173 }
174
175 convenience_http_methods! { get, HttpMethod::Get, "Create a new GET request with the given URL." }
176 convenience_http_methods! { post, HttpMethod::Post, "Create a new POST request with the given URL." }
177 convenience_http_methods! { put, HttpMethod::Put, "Create a new PUT request with the given URL." }
178 convenience_http_methods! { head, HttpMethod::Head, "Create a new HEAD request with the given URL." }
179 convenience_http_methods! { delete, HttpMethod::Delete, "Create a new DELETE request with the given URL." }
180 convenience_http_methods! { patch, HttpMethod::Patch, "Create a new PATCH request with the given URL." }
181
182 pub fn send_all<I>(requests: I) -> Vec<Result<Response, RequestError>>
184 where
185 I: IntoIterator<Item = Request>,
186 {
187 let mut ids: Vec<i32> = Vec::new();
188 let responses = requests
190 .into_iter()
191 .map(|mut r| {
192 r.should_close = false;
193 ids.push(r.rid);
194 Ok(Response::from(r))
195 })
196 .collect();
197
198 let result = unsafe { send_all(ids.as_mut_ptr(), ids.len()) };
199
200 if result == 0 {
201 responses
203 } else {
204 let mut idx = 0;
207 responses
208 .into_iter()
209 .map(|response| {
210 let error = ids.get(idx).and_then(|id| RequestError::from(*id));
211 let result = match error {
212 Some(e) => Err(e),
213 None => response,
214 };
215 idx += 1;
216 result
217 })
218 .collect()
219 }
220 }
221
222 pub fn header<T: AsRef<str>>(mut self, key: T, val: T) -> Self {
224 self.set_header(key, val);
225 self
226 }
227
228 pub fn set_header<T: AsRef<str>>(&mut self, key: T, val: T) {
230 let key = key.as_ref();
231 let val = val.as_ref();
232 unsafe {
233 set_header(self.rid, key.as_ptr(), key.len(), val.as_ptr(), val.len());
234 };
235 }
236
237 pub fn body<T: AsRef<[u8]>>(mut self, data: T) -> Self {
239 self.set_body(data);
240 self
241 }
242
243 pub fn timeout(mut self, value: f64) -> Self {
248 self.set_timeout(value);
249 self
250 }
251
252 pub fn set_body<T: AsRef<[u8]>>(&mut self, data: T) {
254 let data = data.as_ref();
255 unsafe { set_body(self.rid, data.as_ptr(), data.len()) };
256 }
257
258 pub fn set_timeout(&mut self, value: f64) {
263 unsafe { set_timeout(self.rid, value) };
264 }
265
266 pub fn set_url<T: AsRef<str>>(&mut self, url: T) -> Result<(), RequestError> {
268 let url = url.as_ref();
269 self.url = Some(String::from(url));
270 let result = unsafe { set_url(self.rid, url.as_ptr(), url.len()) };
271 if let Some(error) = RequestError::from(result) {
272 Err(error)
273 } else {
274 Ok(())
275 }
276 }
277
278 pub fn url(&self) -> Option<&String> {
280 self.url.as_ref()
281 }
282
283 #[inline]
285 pub fn send(mut self) -> Result<Response, RequestError> {
286 let result = unsafe { send(self.rid) };
287 if let Some(error) = RequestError::from(result) {
288 Err(error)
289 } else {
290 self.should_close = false;
291 Ok(Response::from(self))
292 }
293 }
294
295 pub fn data(self) -> Result<Vec<u8>, RequestError> {
297 self.send()?.get_data()
298 }
299
300 pub fn image(self) -> Result<ImageRef, RequestError> {
302 self.send()?.get_image()
303 }
304
305 pub fn string(self) -> Result<String, AidokuError> {
307 self.send()?.get_string()
308 }
309
310 pub fn html(self) -> Result<Document, RequestError> {
312 self.send()?.get_html()
313 }
314}
315
316#[cfg(feature = "json")]
317impl Request {
318 pub fn json_owned<T>(self) -> Result<T, AidokuError>
320 where
321 T: serde::de::DeserializeOwned,
322 {
323 self.send()?.get_json_owned()
324 }
325}
326
327impl Response {
328 #[inline]
330 pub fn status_code(&self) -> i32 {
331 unsafe { get_status_code(self.rid) }
332 }
333
334 pub fn get_header<T: AsRef<str>>(&self, header: T) -> Option<String> {
336 let header = header.as_ref();
337 let rid = unsafe { get_header(self.rid, header.as_ptr(), header.len()) };
338 if rid < 0 {
339 return None;
340 }
341 read_string_and_destroy(rid)
342 }
343
344 pub fn get_data(&self) -> Result<Vec<u8>, RequestError> {
346 let size = unsafe { data_len(self.rid) };
347 if let Some(error) = RequestError::from(size) {
348 return Err(error);
349 }
350 let size = size as usize;
351 let mut buffer: Vec<u8> = Vec::with_capacity(size);
352 unsafe {
353 let result = read_data(self.rid, buffer.as_mut_ptr(), size);
354 if let Some(error) = RequestError::from(result) {
355 return Err(error);
356 }
357 buffer.set_len(size);
358 }
359 Ok(buffer)
360 }
361
362 pub fn get_image(&self) -> Result<ImageRef, RequestError> {
364 let result = unsafe { get_image(self.rid) };
365 if let Some(error) = RequestError::from(result) {
366 Err(error)
367 } else {
368 Ok(ImageRef::from(result, false))
369 }
370 }
371
372 pub fn get_string(&self) -> Result<String, AidokuError> {
374 let res = String::from_utf8(self.get_data()?);
375 match res {
376 Ok(res) => Ok(res),
377 Err(err) => Err(AidokuError::Utf8Error(err.utf8_error())),
378 }
379 }
380
381 pub fn get_html(&self) -> Result<Document, RequestError> {
383 let rid = unsafe { html(self.rid) };
384 if let Some(error) = RequestError::from(rid) {
385 return Err(error);
386 }
387 Ok(unsafe { Document::from(rid) })
388 }
389
390 pub fn into_request(self) -> Request {
392 let rid = unsafe { init(self.http_method) };
393 if let Some(url) = self.url.as_ref() {
394 _ = unsafe { set_url(rid, url.as_ptr(), url.len()) };
395 }
396 Request {
397 rid,
398 http_method: self.http_method,
399 url: self.url.clone(),
400 should_close: true,
401 }
402 }
403}
404
405#[cfg(feature = "json")]
406impl Response {
407 pub fn get_json<'a, T>(&'a mut self) -> Result<T, AidokuError>
409 where
410 T: serde::de::Deserialize<'a>,
411 {
412 let data = self.get_data()?;
413 self.data = Some(data);
414 let value = serde_json::from_slice(self.data.as_ref().unwrap())?;
415 Ok(value)
416 }
417
418 pub fn get_json_owned<T>(self) -> Result<T, AidokuError>
420 where
421 T: serde::de::DeserializeOwned,
422 {
423 let data = self.get_data()?;
424 let value = serde_json::from_slice(&data)?;
425 Ok(value)
426 }
427}
428
429impl Response {
430 fn from(request: Request) -> Self {
432 Self {
433 rid: request.rid,
434 http_method: request.http_method,
435 url: request.url.clone(),
436 data: None,
437 }
438 }
439}
440
441impl Drop for Request {
442 fn drop(&mut self) {
443 if self.should_close {
444 unsafe { destroy(self.rid) };
445 }
446 }
447}
448
449impl Drop for Response {
450 fn drop(&mut self) {
451 unsafe { destroy(self.rid) };
452 }
453}
454
455pub fn set_rate_limit(permits: i32, period: i32, unit: TimeUnit) {
461 unsafe { net_set_rate_limit(permits, period, unit.into()) }
462}