gistools/util/
fetch.rs

1use alloc::{
2    string::{String, ToString},
3    vec::Vec,
4};
5use serde::Serialize;
6
7/// Net fetch error
8#[derive(Debug, Clone, PartialEq)]
9pub enum NetError {
10    /// Network error
11    Network(String),
12    /// HTTP error
13    Http(u16),
14    /// Other
15    Other(String),
16}
17
18/// HTTP Method (Basic)
19#[derive(Debug, Default, Clone, Copy, PartialEq)]
20pub enum Method {
21    /// GET
22    #[default]
23    Get,
24    /// POST
25    Post,
26    /// PUT
27    Put,
28    /// DELETE
29    Delete,
30    /// HEAD
31    Head,
32    /// OPTIONS
33    Options,
34}
35#[cfg(any(feature = "std", target_arch = "wasm32", feature = "wasm"))]
36impl From<Method> for surf::http::Method {
37    fn from(m: Method) -> surf::http::Method {
38        match m {
39            Method::Get => surf::http::Method::Get,
40            Method::Post => surf::http::Method::Post,
41            Method::Put => surf::http::Method::Put,
42            Method::Delete => surf::http::Method::Delete,
43            Method::Head => surf::http::Method::Head,
44            Method::Options => surf::http::Method::Options,
45        }
46    }
47}
48
49/// STD fetch with arbitrary HTTP method and optional JSON body
50/// Example:
51/// ```ignore
52/// fetch_url("http://example.com/api", &[("header","value")], Some(Method::Post), Some(&my_struct)).await
53/// ```
54#[cfg(feature = "std")]
55pub async fn fetch_url<T: Serialize>(
56    url: &str,
57    headers: &[(&str, &str)],
58    method: Option<Method>,
59    body: Option<&T>,
60) -> Result<Vec<u8>, NetError> {
61    let method = method.unwrap_or_default();
62    let client = surf::client();
63    let url = url.parse::<surf::Url>().map_err(|e| NetError::Other(e.to_string()))?;
64    let mut req = surf::Request::new(method.into(), url);
65
66    // Add headers
67    for (k, v) in headers {
68        req.set_header(*k, *v);
69    }
70    // Add JSON body if provided
71    if let Some(b) = body {
72        let json_str = serde_json::to_string(b).map_err(|e| NetError::Other(e.to_string()))?;
73        req.set_body(surf::Body::from(json_str));
74        req.set_content_type("application/json".into());
75    }
76    // Send request
77    let mut res = client.send(req).await.map_err(|e| NetError::Network(e.to_string()))?;
78
79    if !res.status().is_success() {
80        return Err(NetError::Http(res.status().into()));
81    }
82
83    res.body_bytes().await.map_err(|e| NetError::Other(e.to_string()))
84}
85
86/// WASM fetch for raw data
87/// WASM fetch with arbitrary HTTP method and optional JSON body
88#[cfg(any(target_arch = "wasm32", feature = "wasm"))]
89pub async fn fetch_url<T: Serialize>(
90    url: &str,
91    headers: &[(&str, &str)],
92    method: Option<Method>,
93    body: Option<&T>,
94) -> Result<Vec<u8>, NetError> {
95    let method = method.unwrap_or_default();
96    let client = surf::client();
97    let url = url.parse::<surf::Url>().map_err(|e| NetError::Other(e.to_string()))?;
98    let mut req = surf::Request::new(method.into(), url);
99
100    // Add headers
101    for (k, v) in headers {
102        req.set_header(*k, *v);
103    }
104
105    // Add JSON body if provided
106    if let Some(b) = body {
107        let json_str = serde_json::to_string(b).map_err(|e| NetError::Other(e.to_string()))?;
108        req.set_body(surf::Body::from(json_str));
109        req.set_content_type("application/json".into());
110    }
111
112    // Send request
113    let mut res = client.send(req).await.map_err(|e| NetError::Network(e.to_string()))?;
114
115    if !res.status().is_success() {
116        return Err(NetError::Http(res.status().into()));
117    }
118
119    res.body_bytes().await.map_err(|e| NetError::Other(e.to_string()))
120}