#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::JsCast;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_futures::JsFuture;
#[cfg(target_arch = "wasm32")]
use web_sys::{Request as JsRequest, RequestInit, Response as JsResponse};
pub type NetResult<T> = Result<T, String>;
#[derive(Debug, Clone)]
pub struct Response {
pub status: i32,
pub headers: Vec<(String, String)>,
pub body: String,
}
#[derive(Debug, Clone)]
pub struct Request {
pub url: String,
pub method: String,
pub headers: Vec<(String, String)>,
pub body: Option<String>,
pub timeout: Option<i32>,
}
impl Request {
pub fn get(url: String) -> Self {
Self {
url,
method: "GET".to_string(),
headers: Vec::new(),
body: None,
timeout: None,
}
}
pub fn post(url: String, body: String) -> Self {
Self {
url,
method: "POST".to_string(),
headers: Vec::new(),
body: Some(body),
timeout: None,
}
}
pub fn header(mut self, key: String, value: String) -> Self {
self.headers.push((key, value));
self
}
pub fn timeout(mut self, seconds: i32) -> Self {
self.timeout = Some(seconds);
self
}
pub fn send(self) -> NetResult<Response> {
Err("Blocking requests not available in WASM. Use send_async() instead.".to_string())
}
#[cfg(target_arch = "wasm32")]
pub async fn send_async(self) -> NetResult<Response> {
use web_sys::window;
let window = window().ok_or_else(|| "No window object".to_string())?;
let opts = RequestInit::new();
opts.set_method(&self.method);
if let Some(body) = &self.body {
opts.set_body(&JsValue::from_str(body));
}
let request = JsRequest::new_with_str_and_init(&self.url, &opts)
.map_err(|e| format!("Failed to create request: {:?}", e))?;
let headers = request.headers();
for (key, value) in &self.headers {
headers
.set(key, value)
.map_err(|e| format!("Failed to set header: {:?}", e))?;
}
let resp_value = JsFuture::from(window.fetch_with_request(&request))
.await
.map_err(|e| format!("Fetch failed: {:?}", e))?;
let resp: JsResponse = resp_value
.dyn_into()
.map_err(|_| "Response is not a Response object".to_string())?;
let status = resp.status() as i32;
let headers = Vec::new();
let text_promise = resp
.text()
.map_err(|e| format!("Failed to get response text: {:?}", e))?;
let text_value = JsFuture::from(text_promise)
.await
.map_err(|e| format!("Failed to read response text: {:?}", e))?;
let body = text_value
.as_string()
.ok_or_else(|| "Response text is not a string".to_string())?;
Ok(Response {
status,
headers,
body,
})
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn send_async(self) -> NetResult<Response> {
Err("WASM-only function called in non-WASM context".to_string())
}
}
pub fn get(_url: String) -> NetResult<Response> {
Err("Blocking requests not available in WASM. Use get_async() instead.".to_string())
}
pub fn post(_url: String, _body: String) -> NetResult<Response> {
Err("Blocking requests not available in WASM. Use post_async() instead.".to_string())
}
pub async fn get_async(url: String) -> NetResult<Response> {
Request::get(url).send_async().await
}
pub async fn post_async(url: String, body: String) -> NetResult<Response> {
Request::post(url, body).send_async().await
}
pub fn download(_url: String, _path: String) -> NetResult<()> {
Err(
"File download not available in WASM. Use get_async() and localStorage instead."
.to_string(),
)
}
pub fn upload(_url: String, _path: String) -> NetResult<Response> {
Err(
"File upload not available in WASM. Use post_async() with file contents instead."
.to_string(),
)
}
#[cfg(target_arch = "wasm32")]
pub struct WebSocket {
ws: web_sys::WebSocket,
}
#[cfg(not(target_arch = "wasm32"))]
pub struct WebSocket;
#[cfg(target_arch = "wasm32")]
impl WebSocket {
pub fn connect(url: String) -> NetResult<WebSocket> {
let ws = web_sys::WebSocket::new(&url)
.map_err(|e| format!("Failed to create WebSocket: {:?}", e))?;
Ok(WebSocket { ws })
}
pub fn send(&self, message: String) -> NetResult<()> {
self.ws
.send_with_str(&message)
.map_err(|e| format!("Failed to send message: {:?}", e))?;
Ok(())
}
pub fn receive(&self) -> NetResult<String> {
Err("WebSocket receive requires event listeners (not yet implemented)".to_string())
}
pub fn close(self) -> NetResult<()> {
self.ws
.close()
.map_err(|e| format!("Failed to close WebSocket: {:?}", e))?;
Ok(())
}
}
#[cfg(not(target_arch = "wasm32"))]
impl WebSocket {
pub fn connect(_url: String) -> NetResult<WebSocket> {
Err("WebSocket only available in WASM".to_string())
}
pub fn send(&self, _message: String) -> NetResult<()> {
Err("WebSocket not available".to_string())
}
pub fn receive(&self) -> NetResult<String> {
Err("WebSocket not available".to_string())
}
pub fn close(self) -> NetResult<()> {
Ok(())
}
}