1#[cfg(target_arch = "wasm32")]
4use sapp_jsutils::JsObject;
5
6#[derive(Debug, Clone, PartialEq, Copy)]
7pub enum Method {
8 Post,
9 Put,
10 Get,
11 Delete,
12}
13
14#[derive(Debug)]
15pub enum HttpError {
16 IOError,
17 #[cfg(not(target_arch = "wasm32"))]
18 UreqError(ureq::Error),
19}
20
21impl std::fmt::Display for HttpError {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 match self {
24 HttpError::IOError => write!(f, "IOError"),
25 #[cfg(not(target_arch = "wasm32"))]
26 HttpError::UreqError(error) => write!(f, "Ureq error: {}", error),
27 }
28 }
29}
30impl From<std::io::Error> for HttpError {
31 fn from(_error: std::io::Error) -> HttpError {
32 HttpError::IOError
33 }
34}
35
36#[cfg(not(target_arch = "wasm32"))]
37impl From<ureq::Error> for HttpError {
38 fn from(error: ureq::Error) -> HttpError {
39 HttpError::UreqError(error)
40 }
41}
42
43#[cfg(target_arch = "wasm32")]
44extern "C" {
45 fn http_make_request(scheme: i32, url: JsObject, body: JsObject, headers: JsObject) -> i32;
46 fn http_try_recv(cid: i32) -> JsObject;
47}
48
49#[cfg(not(target_arch = "wasm32"))]
50pub struct Request {
51 rx: std::sync::mpsc::Receiver<Result<String, HttpError>>,
52}
53
54#[cfg(not(target_arch = "wasm32"))]
55impl Request {
56 pub fn try_recv(&mut self) -> Option<Result<String, HttpError>> {
57 self.rx.try_recv().ok()
58 }
59}
60
61#[cfg(target_arch = "wasm32")]
62pub struct Request {
63 cid: i32,
64}
65
66#[cfg(target_arch = "wasm32")]
67impl Request {
68 pub fn try_recv(&mut self) -> Option<Result<String, HttpError>> {
69 let js_obj = unsafe { http_try_recv(self.cid) };
70
71 if js_obj.is_nil() == false {
72 let mut buf = vec![];
73 js_obj.to_byte_buffer(&mut buf);
74
75 let res = std::str::from_utf8(&buf).unwrap().to_owned();
76 return Some(Ok(res));
77 }
78
79 None
80 }
81}
82
83pub struct RequestBuilder {
84 url: String,
85 method: Method,
86 headers: Vec<(String, String)>,
87 body: Option<String>,
88}
89
90impl RequestBuilder {
91 pub fn new(url: &str) -> RequestBuilder {
92 RequestBuilder {
93 url: url.to_owned(),
94 method: Method::Get,
95 headers: vec![],
96 body: None,
97 }
98 }
99
100 pub fn method(self, method: Method) -> RequestBuilder {
101 Self { method, ..self }
102 }
103
104 pub fn header(mut self, header: &str, value: &str) -> RequestBuilder {
105 self.headers.push((header.to_owned(), value.to_owned()));
106
107 Self {
108 headers: self.headers,
109 ..self
110 }
111 }
112
113 pub fn body(self, body: &str) -> RequestBuilder {
114 RequestBuilder {
115 body: Some(body.to_owned()),
116 ..self
117 }
118 }
119
120 #[cfg(not(target_arch = "wasm32"))]
121 pub fn send(self) -> Request {
122 use std::sync::mpsc::channel;
123
124 let (tx, rx) = channel();
125
126 std::thread::spawn(move || {
127 let method = match self.method {
128 Method::Post => ureq::post,
129 Method::Put => ureq::put,
130 Method::Get => ureq::get,
131 Method::Delete => ureq::delete,
132 };
133
134 let mut request = method(&self.url);
135 for (header, value) in self.headers {
136 request = request.set(&header, &value)
137 }
138 let response: Result<String, HttpError> = if let Some(body) = self.body {
139 request.send_string(&body)
140 } else {
141 request.call()
142 }
143 .map_err(|err| err.into())
144 .and_then(|response| response.into_string().map_err(|err| err.into()));
145
146 tx.send(response).unwrap();
147 });
148
149 Request { rx }
150 }
151
152 #[cfg(target_arch = "wasm32")]
153 pub fn send(&self) -> Request {
154 let scheme = match self.method {
155 Method::Post => 0,
156 Method::Put => 1,
157 Method::Get => 2,
158 Method::Delete => 3,
159 };
160
161 let headers = JsObject::object();
162
163 for (header, value) in &self.headers {
164 headers.set_field_string(&header, &value);
165 }
166
167 let cid = unsafe {
168 http_make_request(
169 scheme,
170 JsObject::string(&self.url),
171 JsObject::string(&self.body.as_ref().map(|s| s.as_str()).unwrap_or("")),
172 headers,
173 )
174 };
175 Request { cid }
176 }
177}