axum_browser_adapter/
lib.rs1use std::collections::HashMap;
99use std::str::FromStr;
100use axum::body::Body;
101use axum::http;
102use axum::response::Response;
103use axum::http::{Method, Request, Uri};
104use serde::{Deserialize, Serialize};
105use serde_wasm_bindgen::{from_value, to_value};
106use wasm_bindgen::prelude::*;
107
108pub use axum_wasm_macros::wasm_compat;
109
110#[wasm_bindgen]
111#[derive(Clone, Serialize, Deserialize, Debug)]
112pub struct WasmRequest {
113 #[wasm_bindgen(skip)]
114 pub method: String,
115 #[wasm_bindgen(skip)]
116 pub url: String,
117 #[wasm_bindgen(skip)]
118 pub headers: HashMap<String, String>,
119 #[wasm_bindgen(skip)]
120 pub body: Option<String>,
121}
122
123#[wasm_bindgen]
124impl WasmRequest {
125 #[wasm_bindgen(constructor)]
126 pub fn new(method: String, url: String, headers_js_value: JsValue, body: Option<String>) -> WasmRequest {
127 let headers: HashMap<String, String> = from_value(headers_js_value).unwrap();
128
129 WasmRequest { method, url, headers, body }
130 }
131
132 pub fn append_header(&mut self, key: String, value: String) {
133 self.headers.insert(key, value);
134 }
135}
136
137pub fn wasm_request_to_axum_request(wasm_request: &WasmRequest) -> Result<Request<Body>, Box<dyn std::error::Error>> {
138 let method = Method::from_str(&wasm_request.method)?;
139
140 let uri = Uri::try_from(&wasm_request.url)?;
141
142 let mut request_builder = Request::builder()
143 .method(method)
144 .uri(uri);
145
146 for (k, v) in &wasm_request.headers {
147 let header_name = http::header::HeaderName::from_bytes(k.as_bytes())?;
148 let header_value = http::header::HeaderValue::from_str(v)?;
149 request_builder = request_builder.header(header_name, header_value);
150 }
151
152 let request = match &wasm_request.body {
153 Some(body_str) => request_builder.body(Body::from(body_str.to_owned()))?,
154 None => request_builder.body(Body::empty())?,
155 };
156
157 Ok(request)
158}
159
160#[wasm_bindgen]
161#[derive(Clone, Serialize, Deserialize, Debug)]
162pub struct WasmResponse {
163 #[wasm_bindgen(skip)]
164 pub status_code: String,
165 #[wasm_bindgen(skip)]
166 pub headers: HashMap<String, String>,
167 #[wasm_bindgen(skip)]
168 pub body: Option<String>,
169}
170
171#[wasm_bindgen]
172impl WasmResponse {
173 #[wasm_bindgen(getter)]
174 pub fn status_code(&self) -> String {
175 self.status_code.to_string()
176 }
177
178 #[wasm_bindgen(getter)]
179 pub fn body(&self) -> Option<String> {
180 self.body.clone()
181 }
182
183 #[wasm_bindgen(getter)]
184 pub fn headers(&self) -> JsValue {
185 to_value(&self.headers).unwrap()
186 }
187}
188
189pub async fn axum_response_to_wasm_response(mut response: Response) -> Result<WasmResponse, Box<dyn std::error::Error>> {
190 let status_code = response.status().to_string();
191
192 let mut headers = HashMap::new();
193 for (name, value) in response.headers() {
194 if let Ok(value_str) = value.to_str() {
195 headers.insert(name.as_str().to_owned(), value_str.to_owned());
196 }
197 }
198
199 let bytes = match http_body::Body::data(response.body_mut()).await {
200 None => vec![],
201 Some(body_bytes) => match body_bytes {
202 Ok(bytes) => bytes.to_vec(),
203 Err(_) => vec![]
204 },
205 };
206 let body_str = String::from_utf8(bytes)?;
207
208 Ok(WasmResponse {
209 status_code,
210 headers,
211 body: Some(body_str),
212 })
213}