Fix Routable::parent with hash segments and query params (#3484)
* Fix Routable::parent with hash segments and query params
This commit is contained in:
1 parent
bb3fd38231
commit
57d637184f
2 files changed
+227
-20
No files matched your search
@@ -625,17 +625,43 @@ pub trait Routable: FromStr + Display + Clone + 'static {
|
|||||||
/// ```
|
/// ```
|
||||||
fn is_child_of(&self, other: &Self) -> bool {
|
fn is_child_of(&self, other: &Self) -> bool {
|
||||||
let self_str = self.to_string();
|
let self_str = self.to_string();
|
||||||
let self_str = self_str.trim_matches('/');
|
let self_str = self_str
|
||||||
|
.split_once('#')
|
||||||
|
.map(|(route, _)| route)
|
||||||
|
.unwrap_or(&self_str);
|
||||||
|
let self_str = self_str
|
||||||
|
.split_once('?')
|
||||||
|
.map(|(route, _)| route)
|
||||||
|
.unwrap_or(self_str);
|
||||||
|
let self_str = self_str.trim_end_matches('/');
|
||||||
let other_str = other.to_string();
|
let other_str = other.to_string();
|
||||||
let other_str = other_str.trim_matches('/');
|
let other_str = other_str
|
||||||
if other_str.is_empty() {
|
.split_once('#')
|
||||||
return true;
|
.map(|(route, _)| route)
|
||||||
}
|
.unwrap_or(&other_str);
|
||||||
let self_segments = self_str.split('/');
|
let other_str = other_str
|
||||||
let other_segments = other_str.split('/');
|
.split_once('?')
|
||||||
for (self_seg, other_seg) in self_segments.zip(other_segments) {
|
.map(|(route, _)| route)
|
||||||
if self_seg != other_seg {
|
.unwrap_or(other_str);
|
||||||
return false;
|
let other_str = other_str.trim_end_matches('/');
|
||||||
|
|
||||||
|
let mut self_segments = self_str.split('/');
|
||||||
|
let mut other_segments = other_str.split('/');
|
||||||
|
loop {
|
||||||
|
match (self_segments.next(), other_segments.next()) {
|
||||||
|
// If the two routes are the same length, or this route has less segments, then this segment
|
||||||
|
// cannot be the child of the other segment
|
||||||
|
(None, Some(_)) | (None, None) => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// If two segments are not the same, then this segment cannot be the child of the other segment
|
||||||
|
(Some(self_seg), Some(other_seg)) => {
|
||||||
|
if self_seg != other_seg {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the other route has less segments, then this route is the child of the other route
|
||||||
|
(Some(_), None) => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@@ -667,17 +693,14 @@ pub trait Routable: FromStr + Display + Clone + 'static {
|
|||||||
/// ```
|
/// ```
|
||||||
fn parent(&self) -> Option<Self> {
|
fn parent(&self) -> Option<Self> {
|
||||||
let as_str = self.to_string();
|
let as_str = self.to_string();
|
||||||
let as_str = as_str.trim_matches('/');
|
let (route_and_query, _) = as_str.split_once('#').unwrap_or((&as_str, ""));
|
||||||
let segments = as_str.split('/');
|
let (route, _) = route_and_query
|
||||||
|
.split_once('?')
|
||||||
|
.unwrap_or((route_and_query, ""));
|
||||||
|
let route = route.trim_end_matches('/');
|
||||||
|
let segments = route.split_inclusive('/');
|
||||||
let segment_count = segments.clone().count();
|
let segment_count = segments.clone().count();
|
||||||
let new_route = segments
|
let new_route: String = segments.take(segment_count.saturating_sub(1)).collect();
|
||||||
.take(segment_count - 1)
|
|
||||||
.fold(String::new(), |mut acc, segment| {
|
|
||||||
acc.push('/');
|
|
||||||
acc.push_str(segment);
|
|
||||||
acc
|
|
||||||
});
|
|
||||||
|
|
||||||
Self::from_str(&new_route).ok()
|
Self::from_str(&new_route).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Routable, Clone, PartialEq, Debug)]
|
||||||
|
#[rustfmt::skip]
|
||||||
|
enum Route {
|
||||||
|
#[route("/")]
|
||||||
|
RootIndex {},
|
||||||
|
#[nest("/fixed")]
|
||||||
|
#[layout(Fixed)]
|
||||||
|
#[route("/")]
|
||||||
|
FixedIndex {},
|
||||||
|
#[route("/fixed")]
|
||||||
|
FixedFixed {},
|
||||||
|
#[end_layout]
|
||||||
|
#[end_nest]
|
||||||
|
#[nest("/:id")]
|
||||||
|
#[layout(Parameter)]
|
||||||
|
#[route("/")]
|
||||||
|
ParameterIndex { id: u8 },
|
||||||
|
#[route("/fixed")]
|
||||||
|
ParameterFixed { id: u8 },
|
||||||
|
#[end_layout]
|
||||||
|
#[end_nest]
|
||||||
|
#[nest("/hash")]
|
||||||
|
#[route("/")]
|
||||||
|
HashIndex {},
|
||||||
|
#[nest("/:id")]
|
||||||
|
#[route("/?:query")]
|
||||||
|
HashId { id: u8, query: String },
|
||||||
|
#[layout(Parameter)]
|
||||||
|
#[route("/path/?:query#:hash")]
|
||||||
|
HashQuery { id: u8, query: String, hash: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_parent() {
|
||||||
|
assert_eq!(Route::RootIndex {}.parent(), None);
|
||||||
|
assert_eq!(Route::FixedIndex {}.parent(), Some(Route::RootIndex {}));
|
||||||
|
assert_eq!(Route::FixedFixed {}.parent(), Some(Route::FixedIndex {}));
|
||||||
|
assert_eq!(
|
||||||
|
Route::ParameterIndex { id: 0 }.parent(),
|
||||||
|
Some(Route::RootIndex {})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Route::ParameterFixed { id: 0 }.parent(),
|
||||||
|
Some(Route::ParameterIndex { id: 0 })
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Route::HashQuery {
|
||||||
|
id: 0,
|
||||||
|
query: "query".into(),
|
||||||
|
hash: "hash".into()
|
||||||
|
}
|
||||||
|
.parent(),
|
||||||
|
Some(Route::HashId {
|
||||||
|
id: 0,
|
||||||
|
query: "".into()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Route::HashId {
|
||||||
|
id: 0,
|
||||||
|
query: "query".into()
|
||||||
|
}
|
||||||
|
.parent(),
|
||||||
|
Some(Route::HashIndex {})
|
||||||
|
);
|
||||||
|
assert_eq!(Route::HashIndex {}.parent(), Some(Route::RootIndex {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_child() {
|
||||||
|
assert!(!Route::RootIndex {}.is_child_of(&Route::RootIndex {}));
|
||||||
|
assert!(Route::FixedIndex {}.is_child_of(&Route::RootIndex {}));
|
||||||
|
assert!(!Route::FixedIndex {}.is_child_of(&Route::FixedIndex {}));
|
||||||
|
assert!(Route::FixedFixed {}.is_child_of(&Route::FixedIndex {}));
|
||||||
|
assert!(!Route::FixedFixed {}.is_child_of(&Route::FixedFixed {}));
|
||||||
|
assert!(Route::ParameterIndex { id: 0 }.is_child_of(&Route::RootIndex {}));
|
||||||
|
assert!(!Route::ParameterIndex { id: 0 }.is_child_of(&Route::ParameterIndex { id: 0 }));
|
||||||
|
assert!(Route::ParameterFixed { id: 0 }.is_child_of(&Route::ParameterIndex { id: 0 }));
|
||||||
|
assert!(!Route::ParameterFixed { id: 0 }.is_child_of(&Route::ParameterFixed { id: 0 }));
|
||||||
|
assert!(Route::HashQuery {
|
||||||
|
id: 0,
|
||||||
|
query: "query".into(),
|
||||||
|
hash: "hash".into()
|
||||||
|
}
|
||||||
|
.is_child_of(&Route::HashId {
|
||||||
|
id: 0,
|
||||||
|
query: "query".into()
|
||||||
|
}));
|
||||||
|
assert!(!Route::HashQuery {
|
||||||
|
id: 0,
|
||||||
|
query: "query".into(),
|
||||||
|
hash: "hash".into()
|
||||||
|
}
|
||||||
|
.is_child_of(&Route::HashQuery {
|
||||||
|
id: 0,
|
||||||
|
query: "query".into(),
|
||||||
|
hash: "hash".into()
|
||||||
|
}));
|
||||||
|
assert!(Route::HashId {
|
||||||
|
id: 0,
|
||||||
|
query: "query".into()
|
||||||
|
}
|
||||||
|
.is_child_of(&Route::HashIndex {}));
|
||||||
|
assert!(!Route::HashId {
|
||||||
|
id: 0,
|
||||||
|
query: "query".into()
|
||||||
|
}
|
||||||
|
.is_child_of(&Route::HashId {
|
||||||
|
id: 0,
|
||||||
|
query: "query".into()
|
||||||
|
}));
|
||||||
|
assert!(Route::HashIndex {}.is_child_of(&Route::RootIndex {}));
|
||||||
|
assert!(!Route::HashIndex {}.is_child_of(&Route::HashIndex {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn RootIndex() -> Element {
|
||||||
|
rsx! { h2 { "Root Index" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn Fixed() -> Element {
|
||||||
|
rsx! {
|
||||||
|
h2 { "Fixed" }
|
||||||
|
Outlet::<Route> { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn FixedIndex() -> Element {
|
||||||
|
rsx! { h3 { "Fixed - Index" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn FixedFixed() -> Element {
|
||||||
|
rsx! { h3 { "Fixed - Fixed"} }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn Parameter(id: u8) -> Element {
|
||||||
|
rsx! {
|
||||||
|
h2 { "Parameter {id}" }
|
||||||
|
Outlet::<Route> { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn ParameterIndex(id: u8) -> Element {
|
||||||
|
rsx! { h3 { "Parameter - Index" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn ParameterFixed(id: u8) -> Element {
|
||||||
|
rsx! { h3 { "Parameter - Fixed" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn HashQuery(id: u8, query: String, hash: String) -> Element {
|
||||||
|
rsx! {
|
||||||
|
h2 { "Hash Query" }
|
||||||
|
h3 { "id: {id}" }
|
||||||
|
h3 { "query: {query}" }
|
||||||
|
h3 { "hash: {hash}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn HashIndex() -> Element {
|
||||||
|
rsx! { h3 { "Hash Index" } }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn HashId(id: u8, query: String) -> Element {
|
||||||
|
rsx! {
|
||||||
|
h3 { "Hash Id {id}" }
|
||||||
|
h3 { "query: {query}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user