use crate::js_values;
use crate::{Error, Method};
use serde::de::DeserializeOwned;
use std::borrow::Cow;
use url::Url;
use wasm_bindgen::JsValue;
#[derive(Debug, Clone)]
pub struct Request {
method: Method,
url: Url,
headers: web_sys::Headers,
body: Option<Vec<u8>>,
}
unsafe impl Sync for Request {}
impl Request {
pub fn new(
method: Method,
url: Url,
headers: web_sys::Headers,
body: Option<Vec<u8>>,
) -> Request {
Request {
method,
url,
headers,
body,
}
}
pub(crate) fn from_js(map: &js_sys::Map) -> Result<Self, JsValue> {
Ok(Request::new(
Method::from(
&js_values::get_map_str(&map, "method")
.ok_or_else(|| JsValue::from_str("invalid_req.method"))?,
)?,
Url::parse(
&js_values::get_map_str(&map, "url")
.ok_or_else(|| JsValue::from_str("invalid_req.url"))?,
)
.map_err(|e| JsValue::from_str(&format!("invalid req.url:{}", e.to_string())))?,
js_values::get_map_headers(&map, "headers")
.ok_or_else(|| JsValue::from_str("invalid_req"))?,
js_values::get_map_bytes(&map, "body"),
))
}
pub fn method(&self) -> Method {
self.method
}
pub fn url(&self) -> &Url {
&self.url
}
pub fn headers(&self) -> &web_sys::Headers {
&self.headers
}
pub fn get_header(&self, name: &str) -> Option<String> {
match self.headers.get(name) {
Ok(v) => v,
Err(_) => None,
}
}
pub fn has_header(&self, name: &str) -> bool {
self.headers.has(name).unwrap_or(false)
}
pub fn is_empty(&self) -> bool {
self.body.is_none() || self.body.as_ref().unwrap().is_empty()
}
pub fn body(&self) -> Option<&Vec<u8>> {
self.body.as_ref()
}
pub fn json<T: DeserializeOwned>(&self) -> Result<T, Error> {
if let Some(vec) = self.body.as_ref() {
Ok(serde_json::from_slice(vec)?)
} else {
Err(Error::Other("body is empty".to_string()))
}
}
pub fn get_cookie_value(&self, cookie_name: &str) -> Option<String> {
self.get_header("cookie")
.map(|cookie| {
(&cookie)
.split(';')
.map(|s| s.trim())
.find_map(|part| cookie_value(part, cookie_name))
.map(|v| v.to_string())
})
.unwrap_or_default()
}
pub fn get_query_value<'req>(&'req self, key: &'_ str) -> Option<Cow<'req, str>> {
self.url()
.query_pairs()
.find(|(k, _)| k == key)
.map(|(_, v)| v)
}
}
fn cookie_value<'cookie>(part: &'cookie str, name: &str) -> Option<&'cookie str> {
if part.len() > name.len() {
let (left, right) = part.split_at(name.len());
if left == name && right.starts_with('=') {
return Some(&right[1..]);
}
}
None
}
#[test]
fn test_cookie_value() {
assert_eq!(cookie_value("x=y", "x"), Some("y"));
assert_eq!(cookie_value("foo=bar", "foo"), Some("bar"));
assert_eq!(cookie_value("x=y", "z"), None);
assert_eq!(cookie_value("foo=", "foo"), Some(""));
}