Просмотр исходного кода

Implement router matching for path parameters

We don't want to have the router just always match paths as exact strings. If
a path contains a parameter like "/thing/:id" then the ":id" portion of the
route should match _any_ string, not a literal ":id".
Dave Rolsky 3 лет назад
Родитель
Сommit
f8a7e1c
1 измененных файлов с 57 добавлено и 3 удалено
  1. 57 3
      packages/router/src/service.rs

+ 57 - 3
packages/router/src/service.rs

@@ -72,8 +72,9 @@ impl RouterService {
     }
     }
 
 
     pub fn register_total_route(&self, route: String, scope: ScopeId, fallback: bool) {
     pub fn register_total_route(&self, route: String, scope: ScopeId, fallback: bool) {
-        log::trace!("Registered route '{}' with scope id {:?}", route, scope);
-        self.slots.borrow_mut().push((scope, route));
+        let clean = clean_route(route);
+        log::trace!("Registered route '{}' with scope id {:?}", clean, scope);
+        self.slots.borrow_mut().push((scope, clean));
     }
     }
 
 
     pub fn should_render(&self, scope: ScopeId) -> bool {
     pub fn should_render(&self, scope: ScopeId) -> bool {
@@ -99,7 +100,7 @@ impl RouterService {
                     scope,
                     scope,
                     route,
                     route,
                 );
                 );
-                if route == path {
+                if route_matches_path(route, path) {
                     log::trace!("    and it matches the current path '{}'", path);
                     log::trace!("    and it matches the current path '{}'", path);
                     self.root_found.set(true);
                     self.root_found.set(true);
                     true
                     true
@@ -123,6 +124,59 @@ impl RouterService {
     }
     }
 }
 }
 
 
+fn clean_route(route: String) -> String {
+    if route.as_str() == "/" {
+        return route;
+    }
+    route.trim_end_matches('/').to_string()
+}
+
+fn clean_path(path: &str) -> &str {
+    if path == "/" {
+        return path;
+    }
+    path.trim_end_matches('/')
+}
+
+fn route_matches_path(route: &str, path: &str) -> bool {
+    let route_pieces = route.split('/').collect::<Vec<_>>();
+    let path_pieces = clean_path(path).split('/').collect::<Vec<_>>();
+
+    log::trace!(
+        "  checking route pieces {:?} vs path pieces {:?}",
+        route_pieces,
+        path_pieces,
+    );
+
+    if route_pieces.len() != path_pieces.len() {
+        log::trace!("    the routes are different lengths");
+        return false;
+    }
+
+    for (i, r) in route_pieces.iter().enumerate() {
+        log::trace!("    checking route piece '{}' vs path", r);
+        // If this is a parameter then it matches as long as there's
+        // _any_thing in that spot in the path.
+        if r.starts_with(':') {
+            log::trace!(
+                "      route piece '{}' starts with a colon so it matches anything",
+                r,
+            );
+            continue;
+        }
+        log::trace!(
+            "      route piece '{}' must be an exact match for path piece '{}'",
+            r,
+            path_pieces[i],
+        );
+        if path_pieces[i] != *r {
+            return false;
+        }
+    }
+
+    return true;
+}
+
 pub struct RouterCfg {
 pub struct RouterCfg {
     initial_route: String,
     initial_route: String,
 }
 }