Переглянути джерело

Url decode routes (#1407)

* fix a few new clippy lints

* url decode routes

* fix catch all segments

* fix clippy
ealmloff 1 рік тому
батько
коміт
c8127e164b

+ 2 - 1
packages/router-macro/src/lib.rs

@@ -482,7 +482,8 @@ impl RouteEnum {
                     let route = s;
                     let (route, _hash) = route.split_once('#').unwrap_or((route, ""));
                     let (route, query) = route.split_once('?').unwrap_or((route, ""));
-                    let mut segments = route.split('/');
+                    let query = dioxus_router::exports::urlencoding::decode(query).unwrap_or(query.into());
+                    let mut segments = route.split('/').map(|s| dioxus_router::exports::urlencoding::decode(s).unwrap_or(s.into()));
                     // skip the first empty segment
                     if s.starts_with('/') {
                         let _ = segments.next();

+ 1 - 1
packages/router-macro/src/query.rs

@@ -14,7 +14,7 @@ impl QuerySegment {
         let ident = &self.ident;
         let ty = &self.ty;
         quote! {
-            let #ident = <#ty as dioxus_router::routable::FromQuery>::from_query(query);
+            let #ident = <#ty as dioxus_router::routable::FromQuery>::from_query(&*query);
         }
     }
 

+ 6 - 3
packages/router-macro/src/route_tree.rs

@@ -312,7 +312,8 @@ impl<'a> RouteTreeSegmentData<'a> {
                 quote! {
                     {
                         let mut segments = segments.clone();
-                        if let Some(segment) = segments.next() {
+                        let segment = segments.next();
+                        if let Some(segment) = segment.as_deref() {
                             if #segment == segment {
                                 #(#children)*
                             }
@@ -369,7 +370,7 @@ impl<'a> RouteTreeSegmentData<'a> {
                         quote! {
                             let mut trailing = String::from("/");
                             for seg in segments.clone() {
-                                trailing += seg;
+                                trailing += &*seg;
                                 trailing += "/";
                             }
                             trailing.pop();
@@ -506,7 +507,9 @@ fn return_constructed(
             let remaining_segments = segments.clone();
             let mut segments_clone = segments.clone();
             let next_segment = segments_clone.next();
+            let next_segment = next_segment.as_deref();
             let segment_after_next = segments_clone.next();
+            let segment_after_next = segment_after_next.as_deref();
             match (next_segment, segment_after_next) {
                 // This is the last segment, return the parsed route
                 (None, _) | (Some(""), None) => {
@@ -516,7 +519,7 @@ fn return_constructed(
                 _ => {
                     let mut trailing = String::new();
                     for seg in remaining_segments {
-                        trailing += seg;
+                        trailing += &*seg;
                         trailing += "/";
                     }
                     trailing.pop();

+ 9 - 4
packages/router-macro/src/segment.rs

@@ -59,6 +59,7 @@ impl RouteSegment {
                     {
                         let mut segments = segments.clone();
                         let segment = segments.next();
+                        let segment = segment.as_deref();
                         let parsed = if let Some(#segment) = segment {
                             Ok(())
                         } else {
@@ -80,7 +81,8 @@ impl RouteSegment {
                 quote! {
                     {
                         let mut segments = segments.clone();
-                        let parsed = if let Some(segment) = segments.next() {
+                        let segment = segments.next();
+                        let parsed = if let Some(segment) = segment.as_deref() {
                             <#ty as dioxus_router::routable::FromRouteSegment>::from_route_segment(segment).map_err(|err| #error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name(err)))
                         } else {
                             Err(#error_enum_name::#error_enum_varient(#inner_parse_enum::#missing_error_name))
@@ -100,9 +102,12 @@ impl RouteSegment {
                 quote! {
                     {
                         let parsed = {
-                            let mut segments = segments.clone();
-                            let segments: Vec<_> = segments.collect();
-                            <#ty as dioxus_router::routable::FromRouteSegments>::from_route_segments(&segments).map_err(|err| #error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name(err)))
+                            let remaining_segments: Vec<_> = segments.collect();
+                            let mut new_segments: Vec<&str> = Vec::new();
+                            for segment in &remaining_segments {
+                                new_segments.push(&*segment);
+                            }
+                            <#ty as dioxus_router::routable::FromRouteSegments>::from_route_segments(&new_segments).map_err(|err| #error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name(err)))
                         };
                         match parsed {
                             Ok(#name) => {

+ 1 - 1
packages/router/Cargo.toml

@@ -17,7 +17,7 @@ gloo = { version = "0.8.0", optional = true }
 log = { workspace = true }
 thiserror = { workspace = true }
 futures-util = { workspace = true }
-serde_urlencoded = { version = "0.7.1", optional = true }
+urlencoding = "2.1.3"
 serde = { version = "1", features = ["derive"], optional = true }
 url = "2.3.1"
 wasm-bindgen = { workspace = true, optional = true }

+ 5 - 0
packages/router/src/lib.rs

@@ -82,3 +82,8 @@ pub mod prelude {
 mod utils {
     pub(crate) mod use_router_internal;
 }
+
+#[doc(hidden)]
+pub mod exports {
+    pub use urlencoding;
+}

+ 36 - 3
packages/router/src/routable.rs

@@ -34,7 +34,7 @@ pub trait FromQuery {
 
 impl<T: for<'a> From<&'a str>> FromQuery for T {
     fn from_query(query: &str) -> Self {
-        T::from(query)
+        T::from(&*urlencoding::decode(query).expect("Failed to decode url encoding"))
     }
 }
 
@@ -54,10 +54,22 @@ where
     type Err = <T as FromStr>::Err;
 
     fn from_route_segment(route: &str) -> Result<Self, Self::Err> {
-        T::from_str(route)
+        match urlencoding::decode(route) {
+            Ok(segment) => T::from_str(&segment),
+            Err(err) => {
+                log::error!("Failed to decode url encoding: {}", err);
+                T::from_str(route)
+            }
+        }
     }
 }
 
+#[test]
+fn full_circle() {
+    let route = "testing 1234 hello world";
+    assert_eq!(String::from_route_segment(route).unwrap(), route);
+}
+
 /// Something that can be converted to route segments
 pub trait ToRouteSegments {
     /// Display the route segments
@@ -71,12 +83,33 @@ where
     fn display_route_segements(self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         for segment in self {
             write!(f, "/")?;
-            write!(f, "{}", segment)?;
+            let segment = segment.to_string();
+            match urlencoding::decode(&segment) {
+                Ok(segment) => write!(f, "{}", segment)?,
+                Err(err) => {
+                    log::error!("Failed to decode url encoding: {}", err);
+                    write!(f, "{}", segment)?
+                }
+            }
         }
         Ok(())
     }
 }
 
+#[test]
+fn to_route_segments() {
+    struct DisplaysRoute;
+
+    impl std::fmt::Display for DisplaysRoute {
+        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+            let segments = vec!["hello", "world"];
+            segments.display_route_segements(f)
+        }
+    }
+
+    assert_eq!(DisplaysRoute.to_string(), "/hello/world");
+}
+
 /// Something that can be created from route segments
 pub trait FromRouteSegments: Sized {
     /// The error that can occur when parsing route segments