mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
Merge pull request #343 from killertux/fix/fix-query-params-parser
This commit is contained in:
commit
fc2d6ef19d
5 changed files with 96 additions and 36 deletions
|
@ -19,7 +19,7 @@ pub fn create_location(cx: Scope, path: ReadSignal<String>, state: ReadSignal<St
|
||||||
let pathname = create_memo(cx, move |_| url.with(|url| url.pathname.clone()));
|
let pathname = create_memo(cx, move |_| url.with(|url| url.pathname.clone()));
|
||||||
let search = create_memo(cx, move |_| url.with(|url| url.search.clone()));
|
let search = create_memo(cx, move |_| url.with(|url| url.search.clone()));
|
||||||
let hash = create_memo(cx, move |_| url.with(|url| url.hash.clone()));
|
let hash = create_memo(cx, move |_| url.with(|url| url.hash.clone()));
|
||||||
let query = create_memo(cx, move |_| url.with(|url| url.search_params()));
|
let query = create_memo(cx, move |_| url.with(|url| url.search_params.clone()));
|
||||||
|
|
||||||
Location {
|
Location {
|
||||||
pathname,
|
pathname,
|
||||||
|
|
|
@ -61,7 +61,7 @@ impl Default for ParamsMap {
|
||||||
/// ```
|
/// ```
|
||||||
/// # use leptos_router::params_map;
|
/// # use leptos_router::params_map;
|
||||||
/// let map = params_map! {
|
/// let map = params_map! {
|
||||||
/// "id".to_string() => "1".to_string()
|
/// "id" => "1"
|
||||||
/// };
|
/// };
|
||||||
/// assert_eq!(map.get("id"), Some(&"1".to_string()));
|
/// assert_eq!(map.get("id"), Some(&"1".to_string()));
|
||||||
/// assert_eq!(map.get("missing"), None)
|
/// assert_eq!(map.get("missing"), None)
|
||||||
|
@ -78,7 +78,7 @@ macro_rules! params_map {
|
||||||
let start_capacity = common_macros::const_expr_count!($($key);*);
|
let start_capacity = common_macros::const_expr_count!($($key);*);
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut map = linear_map::LinearMap::with_capacity(start_capacity);
|
let mut map = linear_map::LinearMap::with_capacity(start_capacity);
|
||||||
$( map.insert($key, $val); )*
|
$( map.insert($key.to_string(), $val.to_string()); )*
|
||||||
$crate::ParamsMap(map)
|
$crate::ParamsMap(map)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,20 @@
|
||||||
use crate::ParamsMap;
|
use crate::ParamsMap;
|
||||||
|
#[cfg(not(feature = "ssr"))]
|
||||||
|
use js_sys::{try_iter, Array, JsString};
|
||||||
|
#[cfg(not(feature = "ssr"))]
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
#[cfg(not(feature = "ssr"))]
|
||||||
|
use wasm_bindgen::JsValue;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||||
pub struct Url {
|
pub struct Url {
|
||||||
pub origin: String,
|
pub origin: String,
|
||||||
pub pathname: String,
|
pub pathname: String,
|
||||||
pub search: String,
|
pub search: String,
|
||||||
|
pub search_params: ParamsMap,
|
||||||
pub hash: String,
|
pub hash: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Url {
|
|
||||||
pub fn search_params(&self) -> ParamsMap {
|
|
||||||
let map = self
|
|
||||||
.search
|
|
||||||
.trim_start_matches('?')
|
|
||||||
.split('&')
|
|
||||||
.filter_map(|piece| {
|
|
||||||
let mut parts = piece.split('=');
|
|
||||||
let (k, v) = (parts.next(), parts.next());
|
|
||||||
match k {
|
|
||||||
Some(k) if !k.is_empty() => {
|
|
||||||
Some((unescape(k), unescape(v.unwrap_or_default())))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<linear_map::LinearMap<String, String>>();
|
|
||||||
ParamsMap(map)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "ssr")]
|
|
||||||
pub fn unescape(s: &str) -> String {
|
|
||||||
urlencoding::decode(s)
|
|
||||||
.unwrap_or_else(|_| std::borrow::Cow::from(s))
|
|
||||||
.replace('+', " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "ssr"))]
|
#[cfg(not(feature = "ssr"))]
|
||||||
pub fn unescape(s: &str) -> String {
|
pub fn unescape(s: &str) -> String {
|
||||||
js_sys::decode_uri(s).unwrap().into()
|
js_sys::decode_uri(s).unwrap().into()
|
||||||
|
@ -57,12 +36,24 @@ impl TryFrom<&str> for Url {
|
||||||
|
|
||||||
fn try_from(url: &str) -> Result<Self, Self::Error> {
|
fn try_from(url: &str) -> Result<Self, Self::Error> {
|
||||||
let fake_host = String::from("http://leptos");
|
let fake_host = String::from("http://leptos");
|
||||||
let url = web_sys::Url::new_with_base(url, &fake_host)
|
let url = web_sys::Url::new_with_base(url, &fake_host).map_js_error()?;
|
||||||
.map_err(|e| e.as_string().unwrap_or_default())?;
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
origin: url.origin(),
|
origin: url.origin(),
|
||||||
pathname: url.pathname(),
|
pathname: url.pathname(),
|
||||||
search: url.search(),
|
search: url.search(),
|
||||||
|
search_params: ParamsMap(
|
||||||
|
try_iter(&url.search_params())
|
||||||
|
.map_js_error()?
|
||||||
|
.ok_or("Failed to use URLSearchParams as an iterator".to_string())?
|
||||||
|
.map(|value| {
|
||||||
|
let array: Array = value.map_js_error()?.dyn_into().map_js_error()?;
|
||||||
|
Ok((
|
||||||
|
array.get(0).dyn_into::<JsString>().map_js_error()?.into(),
|
||||||
|
array.get(1).dyn_into::<JsString>().map_js_error()?.into(),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<Result<linear_map::LinearMap<String, String>, Self::Error>>()?,
|
||||||
|
),
|
||||||
hash: url.hash(),
|
hash: url.hash(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -78,7 +69,24 @@ impl TryFrom<&str> for Url {
|
||||||
origin: url.origin().unicode_serialization(),
|
origin: url.origin().unicode_serialization(),
|
||||||
pathname: url.path().to_string(),
|
pathname: url.path().to_string(),
|
||||||
search: url.query().unwrap_or_default().to_string(),
|
search: url.query().unwrap_or_default().to_string(),
|
||||||
|
search_params: ParamsMap(
|
||||||
|
url.query_pairs()
|
||||||
|
.map(|(key, value)| (key.to_string(), value.to_string()))
|
||||||
|
.collect::<linear_map::LinearMap<String, String>>(),
|
||||||
|
),
|
||||||
hash: Default::default(),
|
hash: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "ssr"))]
|
||||||
|
trait MapJsError<T> {
|
||||||
|
fn map_js_error(self) -> Result<T, String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "ssr"))]
|
||||||
|
impl<T> MapJsError<T> for Result<T, JsValue> {
|
||||||
|
fn map_js_error(self) -> Result<T, String> {
|
||||||
|
self.map_err(|e| e.as_string().unwrap_or_default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ cfg_if! {
|
||||||
Some(PathMatch {
|
Some(PathMatch {
|
||||||
path: "/foo/abc-123".into(),
|
path: "/foo/abc-123".into(),
|
||||||
params: params_map!(
|
params: params_map!(
|
||||||
"id".into() => "abc-123".into()
|
"id" => "abc-123"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -72,7 +72,7 @@ cfg_if! {
|
||||||
Some(PathMatch {
|
Some(PathMatch {
|
||||||
path: "/foo/bar".into(),
|
path: "/foo/bar".into(),
|
||||||
params: params_map!(
|
params: params_map!(
|
||||||
"something".into() => "baz/qux".into()
|
"something" => "baz/qux"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -87,7 +87,7 @@ cfg_if! {
|
||||||
Some(PathMatch {
|
Some(PathMatch {
|
||||||
path: "/foo/bar".into(),
|
path: "/foo/bar".into(),
|
||||||
params: params_map!(
|
params: params_map!(
|
||||||
"something".into() => "".into()
|
"something" => ""
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
52
router/tests/query_params.rs
Normal file
52
router/tests/query_params.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use cfg_if::cfg_if;
|
||||||
|
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature = "ssr")] {
|
||||||
|
use leptos_router::{Url, params_map};
|
||||||
|
|
||||||
|
macro_rules! assert_params_map {
|
||||||
|
([$($key:expr => $val:expr),*] , $actual:expr) => (
|
||||||
|
assert_eq!(params_map!($($key => $val),*), $actual)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_param_with_plus_sign() {
|
||||||
|
let url = Url::try_from("http://leptos.com?data=1%2B2%3D3").unwrap();
|
||||||
|
assert_params_map!{
|
||||||
|
["data" => "1+2=3"],
|
||||||
|
url.search_params
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_param_with_ampersand() {
|
||||||
|
let url = Url::try_from("http://leptos.com?data=true+%26+false+%3D+false").unwrap();
|
||||||
|
assert_params_map!{
|
||||||
|
["data" => "true & false = false"],
|
||||||
|
url.search_params
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_complext_query_string() {
|
||||||
|
let url = Url::try_from("http://leptos.com?data=Data%3A+%24+%26+%2B%2B+7").unwrap();
|
||||||
|
assert_params_map!{
|
||||||
|
["data" => "Data: $ & ++ 7"],
|
||||||
|
url.search_params
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multiple_query_params() {
|
||||||
|
let url = Url::try_from("http://leptos.com?param1=value1¶m2=value2").unwrap();
|
||||||
|
assert_params_map!{
|
||||||
|
[
|
||||||
|
"param1" => "value1",
|
||||||
|
"param2" => "value2"
|
||||||
|
],
|
||||||
|
url.search_params
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue