From 5d95893397f81b01b42287998e9ee037326d89df Mon Sep 17 00:00:00 2001 From: Elmer Bulthuis Date: Sat, 13 Jul 2024 16:29:09 +0200 Subject: [PATCH 1/8] use slices --- packages/cargo/goodrouter/src/string_utility.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/cargo/goodrouter/src/string_utility.rs b/packages/cargo/goodrouter/src/string_utility.rs index 3b833a0..32f7eac 100644 --- a/packages/cargo/goodrouter/src/string_utility.rs +++ b/packages/cargo/goodrouter/src/string_utility.rs @@ -1,6 +1,6 @@ use std::cmp; -pub fn find_common_prefix_length(chars_left: &Vec, chars_right: &Vec) -> usize { +pub fn find_common_prefix_length(chars_left: &[char], chars_right: &[char]) -> usize { let common_length = cmp::min(chars_left.len(), chars_right.len()); let mut index = 0; @@ -25,24 +25,24 @@ mod tests { fn common_prefix_length_test() { assert_eq!( find_common_prefix_length( - &String::from("ab").chars().collect(), - &String::from("abc").chars().collect() + &String::from("ab").chars().collect::>(), + &String::from("abc").chars().collect::>() ), 2 ); assert_eq!( find_common_prefix_length( - &String::from("abc").chars().collect(), - &String::from("abc").chars().collect() + &String::from("abc").chars().collect::>(), + &String::from("abc").chars().collect::>() ), 3 ); assert_eq!( find_common_prefix_length( - &String::from("bc").chars().collect(), - &String::from("abc").chars().collect() + &String::from("bc").chars().collect::>(), + &String::from("abc").chars().collect::>() ), 0, ); From 4ff6151b9feeb7841bd4e02213841e1f1a7cc607 Mon Sep 17 00:00:00 2001 From: Elmer Bulthuis Date: Sat, 13 Jul 2024 16:31:37 +0200 Subject: [PATCH 2/8] new module files --- .../cargo/goodrouter/src/{route_node/mod.rs => route_node.rs} | 0 packages/cargo/goodrouter/src/{template/mod.rs => template.rs} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename packages/cargo/goodrouter/src/{route_node/mod.rs => route_node.rs} (100%) rename packages/cargo/goodrouter/src/{template/mod.rs => template.rs} (100%) diff --git a/packages/cargo/goodrouter/src/route_node/mod.rs b/packages/cargo/goodrouter/src/route_node.rs similarity index 100% rename from packages/cargo/goodrouter/src/route_node/mod.rs rename to packages/cargo/goodrouter/src/route_node.rs diff --git a/packages/cargo/goodrouter/src/template/mod.rs b/packages/cargo/goodrouter/src/template.rs similarity index 100% rename from packages/cargo/goodrouter/src/template/mod.rs rename to packages/cargo/goodrouter/src/template.rs From 27434e708b62384323c902c74d9aa7ddeff24474 Mon Sep 17 00:00:00 2001 From: Elmer Bulthuis Date: Sat, 13 Jul 2024 17:18:16 +0200 Subject: [PATCH 3/8] use newtype --- goodrouter.code-workspace | 123 +++++++++--------- packages/cargo/goodrouter/src/route_node.rs | 66 ++++++++-- .../src/route_node/route_node_merge.rs | 45 ++++--- .../src/route_node/route_node_rc.rs | 12 +- .../src/route_node/route_node_utility.rs | 4 +- packages/cargo/goodrouter/src/router.rs | 2 +- 6 files changed, 152 insertions(+), 100 deletions(-) diff --git a/goodrouter.code-workspace b/goodrouter.code-workspace index 1ee30ca..fd2379d 100644 --- a/goodrouter.code-workspace +++ b/goodrouter.code-workspace @@ -1,64 +1,67 @@ { - "folders": [ - { - "name": "assets", - "path": "assets", - }, - { - "name": "fixtures", - "path": "fixtures", - }, - { - "name": "npm/www", - "path": "packages/npm/www", - }, - { - "name": "npm/goodrouter", - "path": "packages/npm/goodrouter", - }, - { - "name": "cargo/goodrouter", - "path": "packages/cargo/goodrouter", - }, - { - "name": "net/Goodrouter", - "path": "packages/net/Goodrouter", - }, - { - "name": "net/Goodrouter.Spec", - "path": "packages/net/Goodrouter.Spec", - }, - { - "name": "net/Goodrouter.Bench", - "path": "packages/net/Goodrouter.Bench", - }, - ], - "settings": { - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.fixAll": "always", - "source.organizeImports": "always", - }, - "editor.rulers": [100], - "editor.defaultFormatter": "esbenp.prettier-vscode", - "rust-analyzer.check.command": "clippy", - "typescript.tsdk": "./node_modules/typescript/lib", - "npm.packageManager": "npm", + "folders": [ + { + "name": "assets", + "path": "assets", + }, + { + "name": "fixtures", + "path": "fixtures", + }, + { + "name": "npm/www", + "path": "packages/npm/www", + }, + { + "name": "npm/goodrouter", + "path": "packages/npm/goodrouter", + }, + { + "name": "cargo/goodrouter", + "path": "packages/cargo/goodrouter", }, - "extensions": { - "recommendations": [ - "editorconfig.editorconfig", - "ryanluker.vscode-coverage-gutters", - "tamasfe.even-better-toml", - "ms-dotnettools.csharp", - "redhat.vscode-xml", - "streetsidesoftware.code-spell-checker", - "rust-lang.rust-analyzer", - "vadimcn.vscode-lldb", - "esbenp.prettier-vscode", - "eseom.nunjucks-template", - "firefox-devtools.vscode-firefox-debug", - "msjsdiag.debugger-for-chrome", - ], + { + "name": "net/Goodrouter", + "path": "packages/net/Goodrouter", }, + { + "name": "net/Goodrouter.Spec", + "path": "packages/net/Goodrouter.Spec", + }, + { + "name": "net/Goodrouter.Bench", + "path": "packages/net/Goodrouter.Bench", + }, + ], + "settings": { + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll": "always", + "source.organizeImports": "always", + }, + "editor.rulers": [100], + "editor.defaultFormatter": "esbenp.prettier-vscode", + "rust-analyzer.check.command": "clippy", + "typescript.tsdk": "./node_modules/typescript/lib", + "npm.packageManager": "npm", + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer", + }, + }, + "extensions": { + "recommendations": [ + "editorconfig.editorconfig", + "ryanluker.vscode-coverage-gutters", + "tamasfe.even-better-toml", + "ms-dotnettools.csharp", + "redhat.vscode-xml", + "streetsidesoftware.code-spell-checker", + "rust-lang.rust-analyzer", + "vadimcn.vscode-lldb", + "esbenp.prettier-vscode", + "eseom.nunjucks-template", + "firefox-devtools.vscode-firefox-debug", + "msjsdiag.debugger-for-chrome", + ], + }, } diff --git a/packages/cargo/goodrouter/src/route_node.rs b/packages/cargo/goodrouter/src/route_node.rs index 790bd51..926b879 100644 --- a/packages/cargo/goodrouter/src/route_node.rs +++ b/packages/cargo/goodrouter/src/route_node.rs @@ -3,15 +3,13 @@ pub mod route_node_rc; pub mod route_node_utility; use route_node_utility::*; -use std::{ - cell::RefCell, - cmp::Ordering, - collections::BTreeSet, - rc::{Rc, Weak}, -}; +use std::{cell::RefCell, cmp::Ordering, collections::BTreeSet, rc}; -pub type RouteNodeRc<'r, K> = Rc>>; -pub type RouteNodeWeak<'r, K> = Weak>>; +#[derive(Debug)] +pub struct RouteNodeRc<'r, K>(pub rc::Rc>>); + +#[derive(Debug)] +pub struct RouteNodeWeak<'r, K>(pub rc::Weak>>); #[derive(Debug)] pub struct RouteNode<'r, K> { @@ -29,6 +27,26 @@ pub struct RouteNode<'r, K> { parent: Option>, } +impl<'r, K> TryFrom<&RouteNodeWeak<'r, K>> for RouteNodeRc<'r, K> { + type Error = (); + + fn try_from(value: &RouteNodeWeak<'r, K>) -> Result { + Ok(Self(value.0.upgrade().ok_or(())?)) + } +} + +impl<'r, K> From> for RouteNodeRc<'r, K> { + fn from(value: RouteNode<'r, K>) -> Self { + Self(rc::Rc::new(RefCell::new(value))) + } +} + +impl<'r, K> From<&RouteNodeRc<'r, K>> for RouteNodeWeak<'r, K> { + fn from(value: &RouteNodeRc<'r, K>) -> Self { + Self(rc::Rc::downgrade(&value.0)) + } +} + impl<'r, K> Ord for RouteNode<'r, K> { fn cmp(&self, other: &Self) -> Ordering { if self.anchor.len() < other.anchor.len() { @@ -62,6 +80,18 @@ impl<'r, K> PartialOrd for RouteNode<'r, K> { } } +impl<'r, K> Ord for RouteNodeRc<'r, K> { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +impl<'r, K> PartialOrd for RouteNodeRc<'r, K> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + impl<'r, K> Eq for RouteNode<'r, K> {} impl<'r, K> PartialEq for RouteNode<'r, K> { @@ -70,6 +100,14 @@ impl<'r, K> PartialEq for RouteNode<'r, K> { } } +impl<'r, K> Eq for RouteNodeRc<'r, K> {} + +impl<'r, K> PartialEq for RouteNodeRc<'r, K> { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + impl<'r, K> Default for RouteNode<'r, K> { fn default() -> Self { Self { @@ -83,6 +121,18 @@ impl<'r, K> Default for RouteNode<'r, K> { } } +impl<'r, K> Default for RouteNodeRc<'r, K> { + fn default() -> Self { + Self(Default::default()) + } +} + +impl<'r, K> Clone for RouteNodeRc<'r, K> { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/packages/cargo/goodrouter/src/route_node/route_node_merge.rs b/packages/cargo/goodrouter/src/route_node/route_node_merge.rs index 436c7b2..87237fd 100644 --- a/packages/cargo/goodrouter/src/route_node/route_node_merge.rs +++ b/packages/cargo/goodrouter/src/route_node/route_node_merge.rs @@ -1,5 +1,4 @@ use super::*; -use std::{cell::RefCell, rc::Rc}; pub fn route_node_merge<'r, K>( parent_node_rc: RouteNodeRc<'r, K>, @@ -12,7 +11,7 @@ pub fn route_node_merge<'r, K>( ) -> RouteNodeRc<'r, K> { if let Some(child_node_rc) = child_node_rc { let common_prefix = &anchor[..common_prefix_length]; - let child_anchor = child_node_rc.borrow().anchor; + let child_anchor = child_node_rc.0.borrow().anchor; if child_anchor == anchor { return route_node_merge_join(child_node_rc, route_key, route_parameter_names.clone()); @@ -70,12 +69,12 @@ fn route_node_merge_new<'r, K>( has_parameter, route_key, route_parameter_names, - parent: Some(Rc::downgrade(&parent_node_rc)), + parent: Some((&parent_node_rc).into()), ..Default::default() }; - let node_new_rc = Rc::new(RefCell::new(new_node)); - let mut parent_node = parent_node_rc.borrow_mut(); + let node_new_rc: RouteNodeRc<_> = new_node.into(); + let mut parent_node = parent_node_rc.0.borrow_mut(); parent_node.children.insert(node_new_rc.clone()); node_new_rc @@ -86,7 +85,7 @@ fn route_node_merge_join<'r, K>( route_key: Option, route_parameter_names: Vec<&'r str>, ) -> RouteNodeRc<'r, K> { - let mut child_node = child_node_rc.borrow_mut(); + let mut child_node = child_node_rc.0.borrow_mut(); if child_node.route_key.is_some() && route_key.is_some() { panic!("ambiguous route") @@ -117,22 +116,22 @@ fn route_node_merge_intermediate<'r, K>( ..Default::default() }; - let new_node_rc = Rc::new(RefCell::new(new_node)); + let new_node_rc: RouteNodeRc<_> = new_node.into(); // remove the child from parent { - let mut parent_node = parent_node_rc.borrow_mut(); + let mut parent_node = parent_node_rc.0.borrow_mut(); parent_node.children.remove(&child_node_rc); } // create an intermediate node let intermediate_node_rc = { - let child_node = child_node_rc.borrow(); + let child_node = child_node_rc.0.borrow(); let mut intermediate_node = RouteNode { anchor: &child_node.anchor[..common_prefix_length], has_parameter: child_node.has_parameter, - parent: Some(Rc::downgrade(&parent_node_rc)), + parent: Some((&parent_node_rc).into()), ..Default::default() }; @@ -140,9 +139,9 @@ fn route_node_merge_intermediate<'r, K>( intermediate_node.children.insert(new_node_rc.clone()); // insert the intermediate node - let mut parent_node = parent_node_rc.borrow_mut(); + let mut parent_node = parent_node_rc.0.borrow_mut(); - let intermediate_node_rc = Rc::new(RefCell::new(intermediate_node)); + let intermediate_node_rc: RouteNodeRc<_> = intermediate_node.into(); parent_node.children.insert(intermediate_node_rc.clone()); intermediate_node_rc @@ -150,14 +149,14 @@ fn route_node_merge_intermediate<'r, K>( // update the new and child nodes { - let mut child_node = child_node_rc.borrow_mut(); - let mut new_node = new_node_rc.borrow_mut(); + let mut child_node = child_node_rc.0.borrow_mut(); + let mut new_node = new_node_rc.0.borrow_mut(); - new_node.parent = Some(Rc::downgrade(&intermediate_node_rc)); + new_node.parent = Some((&intermediate_node_rc).into()); new_node.anchor = &new_node.anchor[common_prefix_length..]; new_node.has_parameter = false; - child_node.parent = Some(Rc::downgrade(&intermediate_node_rc)); + child_node.parent = Some((&intermediate_node_rc).into()); child_node.anchor = &child_node.anchor[common_prefix_length..]; child_node.has_parameter = false; } @@ -179,7 +178,7 @@ fn route_node_merge_add_to_child<'r, K>( let has_parameter = false; let (common_prefix_length2, child_node_rc2) = - route_node_find_similar_child(&child_node_rc.borrow(), anchor, has_parameter); + route_node_find_similar_child(&child_node_rc.0.borrow(), anchor, has_parameter); return route_node_merge( child_node_rc.clone(), @@ -208,21 +207,21 @@ fn route_node_merge_add_to_new<'r, K>( route_parameter_names, ..Default::default() }; - let new_node_rc = Rc::new(RefCell::new(new_node)); + let new_node_rc: RouteNodeRc<_> = new_node.into(); - let mut parent_node = parent_node_rc.borrow_mut(); + let mut parent_node = parent_node_rc.0.borrow_mut(); parent_node.children.remove(&child_node_rc); parent_node.children.insert(new_node_rc.clone()); - let mut new_node = new_node_rc.borrow_mut(); + let mut new_node = new_node_rc.0.borrow_mut(); new_node.children.insert(child_node_rc.clone()); - new_node.parent = Some(Rc::downgrade(&parent_node_rc)); + new_node.parent = Some((&parent_node_rc).into()); - let mut child_node = child_node_rc.borrow_mut(); + let mut child_node = child_node_rc.0.borrow_mut(); child_node.anchor = &child_node.anchor[common_prefix_length..]; child_node.has_parameter = false; - child_node.parent = Some(Rc::downgrade(&new_node_rc)); + child_node.parent = Some((&new_node_rc).into()); new_node_rc.clone() } diff --git a/packages/cargo/goodrouter/src/route_node/route_node_rc.rs b/packages/cargo/goodrouter/src/route_node/route_node_rc.rs index 8cbdfd4..9d9984a 100644 --- a/packages/cargo/goodrouter/src/route_node/route_node_rc.rs +++ b/packages/cargo/goodrouter/src/route_node/route_node_rc.rs @@ -13,7 +13,7 @@ pub fn route_node_parse<'r, 'f, K: Copy>( let mut path = path; let mut parameter_values: Vec<&str> = Default::default(); - let node = node_rc.borrow(); + let node = node_rc.0.borrow(); if node.has_parameter { // we are matching a parameter value! If the path's length is 0, there is no match, because a parameter value should have at least length 1 @@ -93,7 +93,7 @@ where let mut path_parts = Vec::new(); while let Some(node_rc) = current_node_rc { - let node = node_rc.borrow(); + let node = node_rc.0.borrow(); path_parts.insert(0, Cow::Borrowed(node.anchor)); if node.has_parameter { @@ -104,7 +104,7 @@ where current_node_rc = node .parent .as_ref() - .map(|parent_node_weak| parent_node_weak.upgrade().unwrap()); + .map(|parent_node_weak| parent_node_weak.try_into().unwrap()); } path_parts @@ -137,7 +137,7 @@ pub fn route_node_insert<'r, K: Copy>( }; let (common_prefix_length, child_node_rc) = - route_node_find_similar_child(&node_current_rc.borrow(), anchor, has_parameter); + route_node_find_similar_child(&node_current_rc.0.borrow(), anchor, has_parameter); node_current_rc = route_node_merge( node_current_rc, @@ -166,7 +166,7 @@ mod tests { let mut node_root_previous_rc = None; for route_configs in route_configs.iter().permutations(route_configs.len()) { - let node_root_rc = Rc::new(RefCell::new(RouteNode::default())); + let node_root_rc = RouteNodeRc::default(); for template in route_configs { route_node_insert( @@ -178,7 +178,7 @@ mod tests { } { - let node_root = node_root_rc.borrow(); + let node_root = node_root_rc.0.borrow(); assert_eq!(node_root.children.len(), 1); } diff --git a/packages/cargo/goodrouter/src/route_node/route_node_utility.rs b/packages/cargo/goodrouter/src/route_node/route_node_utility.rs index ad1b8dd..e7295fc 100644 --- a/packages/cargo/goodrouter/src/route_node/route_node_utility.rs +++ b/packages/cargo/goodrouter/src/route_node/route_node_utility.rs @@ -9,11 +9,11 @@ pub fn route_node_find_similar_child<'r, K>( let anchor_chars: Vec<_> = anchor.chars().collect(); for child_node_rc in parent_node.children.iter() { - if child_node_rc.borrow().has_parameter != has_parameter { + if child_node_rc.0.borrow().has_parameter != has_parameter { continue; } - let child_anchor_chars: Vec<_> = child_node_rc.borrow().anchor.chars().collect(); + let child_anchor_chars: Vec<_> = child_node_rc.0.borrow().anchor.chars().collect(); let common_prefix_length = find_common_prefix_length(&anchor_chars, &child_anchor_chars); diff --git a/packages/cargo/goodrouter/src/router.rs b/packages/cargo/goodrouter/src/router.rs index 57ec233..8a0922d 100644 --- a/packages/cargo/goodrouter/src/router.rs +++ b/packages/cargo/goodrouter/src/router.rs @@ -110,7 +110,7 @@ impl<'r, K: Eq + Hash + Copy> Router<'r, K> { 'r: 'f, { if let Some(node_rc) = self.leaf_nodes_rc.get(&route_key) { - let parameter_values: Vec<_> = node_rc + let parameter_values: Vec<_> = node_rc.0 .borrow() .route_parameter_names .iter() From 40f872b836fb4d0d5ef847b9c866024abbb5494b Mon Sep 17 00:00:00 2001 From: Elmer Bulthuis Date: Sat, 13 Jul 2024 17:29:45 +0200 Subject: [PATCH 4/8] impls --- packages/cargo/goodrouter/src/route_node.rs | 1 - .../src/route_node/route_node_merge.rs | 6 +- .../src/route_node/route_node_rc.rs | 272 +++++++++--------- .../src/route_node/route_node_utility.rs | 36 +-- packages/cargo/goodrouter/src/router.rs | 28 +- 5 files changed, 170 insertions(+), 173 deletions(-) diff --git a/packages/cargo/goodrouter/src/route_node.rs b/packages/cargo/goodrouter/src/route_node.rs index 926b879..ea4d80a 100644 --- a/packages/cargo/goodrouter/src/route_node.rs +++ b/packages/cargo/goodrouter/src/route_node.rs @@ -2,7 +2,6 @@ pub mod route_node_merge; pub mod route_node_rc; pub mod route_node_utility; -use route_node_utility::*; use std::{cell::RefCell, cmp::Ordering, collections::BTreeSet, rc}; #[derive(Debug)] diff --git a/packages/cargo/goodrouter/src/route_node/route_node_merge.rs b/packages/cargo/goodrouter/src/route_node/route_node_merge.rs index 87237fd..d5afe47 100644 --- a/packages/cargo/goodrouter/src/route_node/route_node_merge.rs +++ b/packages/cargo/goodrouter/src/route_node/route_node_merge.rs @@ -177,8 +177,10 @@ fn route_node_merge_add_to_child<'r, K>( let anchor = &anchor[common_prefix_length..]; let has_parameter = false; - let (common_prefix_length2, child_node_rc2) = - route_node_find_similar_child(&child_node_rc.0.borrow(), anchor, has_parameter); + let (common_prefix_length2, child_node_rc2) = child_node_rc + .0 + .borrow() + .find_similar_child(anchor, has_parameter); return route_node_merge( child_node_rc.clone(), diff --git a/packages/cargo/goodrouter/src/route_node/route_node_rc.rs b/packages/cargo/goodrouter/src/route_node/route_node_rc.rs index 9d9984a..e6c5f2e 100644 --- a/packages/cargo/goodrouter/src/route_node/route_node_rc.rs +++ b/packages/cargo/goodrouter/src/route_node/route_node_rc.rs @@ -5,152 +5,159 @@ use regex::Regex; use std::borrow::Cow; use std::cmp::min; -pub fn route_node_parse<'r, 'f, K: Copy>( - node_rc: RouteNodeRc<'r, K>, - path: &'f str, - maximum_parameter_value_length: usize, -) -> (Option, Vec<&'r str>, Vec<&'f str>) { - let mut path = path; - let mut parameter_values: Vec<&str> = Default::default(); - - let node = node_rc.0.borrow(); - - if node.has_parameter { - // we are matching a parameter value! If the path's length is 0, there is no match, because a parameter value should have at least length 1 - if path.is_empty() { - return Default::default(); - } - - // look for the anchor in the path. If the anchor is empty, match the remainder of the path - let index = if node.anchor.is_empty() { - Some(path.len()) - } else { - path[..min( - maximum_parameter_value_length + node.anchor.len(), - path.len(), - )] - .find(node.anchor) - }; - - if let Some(index) = index { - let value = &path[..index]; +impl<'r, K> RouteNodeRc<'r, K> { + pub fn parse<'f>( + &self, + path: &'f str, + maximum_parameter_value_length: usize, + ) -> (Option, Vec<&'r str>, Vec<&'f str>) + where + K: Copy, + { + let mut path = path; + let mut parameter_values: Vec<&str> = Default::default(); + + let node = self.0.borrow(); - // remove the matches part from the path - path = &path[index + node.anchor.len()..]; + if node.has_parameter { + // we are matching a parameter value! If the path's length is 0, there is no match, because a parameter value should have at least length 1 + if path.is_empty() { + return Default::default(); + } - parameter_values.push(value); + // look for the anchor in the path. If the anchor is empty, match the remainder of the path + let index = if node.anchor.is_empty() { + Some(path.len()) + } else { + path[..min( + maximum_parameter_value_length + node.anchor.len(), + path.len(), + )] + .find(node.anchor) + }; + + if let Some(index) = index { + let value = &path[..index]; + + // remove the matches part from the path + path = &path[index + node.anchor.len()..]; + + parameter_values.push(value); + } else { + return Default::default(); + } } else { - return Default::default(); - } - } else { - // if this node does not represent a parameter we expect the path to start with the `anchor` - if !path.starts_with(node.anchor) { - // this node does not match the path - return Default::default(); - } + // if this node does not represent a parameter we expect the path to start with the `anchor` + if !path.starts_with(node.anchor) { + // this node does not match the path + return Default::default(); + } - // we successfully matches the node to the path, now remove the matched part from the path - path = &path[node.anchor.len()..]; - } + // we successfully matches the node to the path, now remove the matched part from the path + path = &path[node.anchor.len()..]; + } - for child_rc in &node.children { - if let (Some(child_route_name), child_route_parameter_names, mut child_parameters_values) = - route_node_parse(child_rc.clone(), path, maximum_parameter_value_length) - { - let mut parameter_values = parameter_values.clone(); - parameter_values.append(&mut child_parameters_values); - return ( - Some(child_route_name), - child_route_parameter_names, - parameter_values, - ); + for child_rc in &node.children { + if let (Some(child_route_name), child_route_parameter_names, mut child_parameters_values) = + child_rc.parse(path, maximum_parameter_value_length) + { + let mut parameter_values = parameter_values.clone(); + parameter_values.append(&mut child_parameters_values); + return ( + Some(child_route_name), + child_route_parameter_names, + parameter_values, + ); + } } - } - // if the node had a route name and there is no path left to match against then we found a route - if path.is_empty() { - if let Some(route_key) = node.route_key { - return ( - Some(route_key), - node.route_parameter_names.clone(), - parameter_values, - ); + // if the node had a route name and there is no path left to match against then we found a route + if path.is_empty() { + if let Some(route_key) = node.route_key { + return ( + Some(route_key), + node.route_parameter_names.clone(), + parameter_values, + ); + } } - } - Default::default() -} + Default::default() + } -pub fn route_node_stringify<'r, 'f, K>( - node_rc: RouteNodeRc<'r, K>, - parameter_values: Vec>, -) -> Cow<'f, str> -where - 'r: 'f, -{ - let mut parameter_values = parameter_values.clone(); - let mut current_node_rc = Some(node_rc); - let mut path_parts = Vec::new(); - - while let Some(node_rc) = current_node_rc { - let node = node_rc.0.borrow(); - path_parts.insert(0, Cow::Borrowed(node.anchor)); + pub fn stringify<'f>(&self, parameter_values: Vec>) -> Cow<'f, str> + where + 'r: 'f, + { + let mut parameter_values = parameter_values.clone(); + let mut current_node_rc = Some(self.clone()); + let mut path_parts = Vec::new(); + + while let Some(node_rc) = current_node_rc { + let node = node_rc.0.borrow(); + path_parts.insert(0, Cow::Borrowed(node.anchor)); + + if node.has_parameter { + let value = parameter_values.pop().unwrap(); + path_parts.insert(0, value); + } - if node.has_parameter { - let value = parameter_values.pop().unwrap(); - path_parts.insert(0, value); + current_node_rc = node + .parent + .as_ref() + .map(|parent_node_weak| parent_node_weak.try_into().unwrap()); } - current_node_rc = node - .parent - .as_ref() - .map(|parent_node_weak| parent_node_weak.try_into().unwrap()); + path_parts + .into_iter() + .reduce(|path, path_part| path + path_part) + .unwrap() } - path_parts - .into_iter() - .reduce(|path, path_part| path + path_part) - .unwrap() -} + pub fn insert( + &self, + route_key: K, + template: &'r str, + parameter_placeholder_re: &'r Regex, + ) -> RouteNodeRc<'r, K> + where + K: Copy, + { + let template_pairs: Vec<_> = parse_template_pairs(template, parameter_placeholder_re).collect(); + let route_parameter_names: Vec<_> = template_pairs + .iter() + .cloned() + .filter_map(|(_anchor, parameter)| parameter) + .collect(); + + let mut node_current_rc = self.clone(); + for index in 0..template_pairs.len() { + let (anchor, parameter) = template_pairs[index]; + let has_parameter = parameter.is_some(); + let route_key = if index == template_pairs.len() - 1 { + Some(route_key) + } else { + None + }; + + let (common_prefix_length, child_node_rc) = node_current_rc + .0 + .borrow() + .find_similar_child(anchor, has_parameter); + + node_current_rc = route_node_merge( + node_current_rc, + child_node_rc, + anchor, + has_parameter, + route_key, + route_parameter_names.clone(), + common_prefix_length, + ); + } -pub fn route_node_insert<'r, K: Copy>( - root_node_rc: RouteNodeRc<'r, K>, - route_key: K, - template: &'r str, - parameter_placeholder_re: &'r Regex, -) -> RouteNodeRc<'r, K> { - let template_pairs: Vec<_> = parse_template_pairs(template, parameter_placeholder_re).collect(); - let route_parameter_names: Vec<_> = template_pairs - .iter() - .cloned() - .filter_map(|(_anchor, parameter)| parameter) - .collect(); - - let mut node_current_rc = root_node_rc.clone(); - for index in 0..template_pairs.len() { - let (anchor, parameter) = template_pairs[index]; - let has_parameter = parameter.is_some(); - let route_key = if index == template_pairs.len() - 1 { - Some(route_key) - } else { - None - }; - - let (common_prefix_length, child_node_rc) = - route_node_find_similar_child(&node_current_rc.0.borrow(), anchor, has_parameter); - - node_current_rc = route_node_merge( - node_current_rc, - child_node_rc, - anchor, - has_parameter, - route_key, - route_parameter_names.clone(), - common_prefix_length, - ); + node_current_rc } - - node_current_rc } #[cfg(test)] @@ -169,12 +176,7 @@ mod tests { let node_root_rc = RouteNodeRc::default(); for template in route_configs { - route_node_insert( - node_root_rc.clone(), - template, - template, - &TEMPLATE_PLACEHOLDER_REGEX, - ); + node_root_rc.insert(template, template, &TEMPLATE_PLACEHOLDER_REGEX); } { diff --git a/packages/cargo/goodrouter/src/route_node/route_node_utility.rs b/packages/cargo/goodrouter/src/route_node/route_node_utility.rs index e7295fc..22f9c3f 100644 --- a/packages/cargo/goodrouter/src/route_node/route_node_utility.rs +++ b/packages/cargo/goodrouter/src/route_node/route_node_utility.rs @@ -1,28 +1,30 @@ use super::*; use crate::string_utility::find_common_prefix_length; -pub fn route_node_find_similar_child<'r, K>( - parent_node: &RouteNode<'r, K>, - anchor: &'r str, - has_parameter: bool, -) -> (usize, Option>) { - let anchor_chars: Vec<_> = anchor.chars().collect(); +impl<'r, K> RouteNode<'r, K> { + pub fn find_similar_child( + &self, + anchor: &'r str, + has_parameter: bool, + ) -> (usize, Option>) { + let anchor_chars: Vec<_> = anchor.chars().collect(); - for child_node_rc in parent_node.children.iter() { - if child_node_rc.0.borrow().has_parameter != has_parameter { - continue; - } + for child_node_rc in self.children.iter() { + if child_node_rc.0.borrow().has_parameter != has_parameter { + continue; + } + + let child_anchor_chars: Vec<_> = child_node_rc.0.borrow().anchor.chars().collect(); - let child_anchor_chars: Vec<_> = child_node_rc.0.borrow().anchor.chars().collect(); + let common_prefix_length = find_common_prefix_length(&anchor_chars, &child_anchor_chars); - let common_prefix_length = find_common_prefix_length(&anchor_chars, &child_anchor_chars); + if common_prefix_length == 0 { + continue; + } - if common_prefix_length == 0 { - continue; + return (common_prefix_length, Some(child_node_rc.clone())); } - return (common_prefix_length, Some(child_node_rc.clone())); + Default::default() } - - Default::default() } diff --git a/packages/cargo/goodrouter/src/router.rs b/packages/cargo/goodrouter/src/router.rs index 8a0922d..798c476 100644 --- a/packages/cargo/goodrouter/src/router.rs +++ b/packages/cargo/goodrouter/src/router.rs @@ -1,8 +1,4 @@ -use crate::{ - route_node::route_node_rc::{route_node_insert, route_node_parse, route_node_stringify}, - route_node::RouteNodeRc, - template::TEMPLATE_PLACEHOLDER_REGEX, -}; +use crate::{route_node::RouteNodeRc, template::TEMPLATE_PLACEHOLDER_REGEX}; use regex::Regex; use std::hash::Hash; use std::{borrow::Cow, collections::HashMap}; @@ -66,23 +62,18 @@ impl<'r, K: Eq + Hash + Copy> Router<'r, K> { } pub fn insert_route(&mut self, route_key: K, template: &'r str) -> &mut Self { - let leaf_node_rc = route_node_insert( - self.root_node_rc.clone(), - route_key, - template, - self.parameter_placeholder_re, - ); + let leaf_node_rc = self + .root_node_rc + .insert(route_key, template, self.parameter_placeholder_re); self.leaf_nodes_rc.insert(route_key, leaf_node_rc); self } pub fn parse_route<'f>(&self, path: &'f str) -> (Option, HashMap<&'r str, Cow<'f, str>>) { - let (route_key, parameter_names, parameter_values) = route_node_parse( - self.root_node_rc.clone(), - path, - self.maximum_parameter_value_length, - ); + let (route_key, parameter_names, parameter_values) = self + .root_node_rc + .parse(path, self.maximum_parameter_value_length); if let Some(route_key) = route_key { let parameters: HashMap<_, _> = parameter_names @@ -110,7 +101,8 @@ impl<'r, K: Eq + Hash + Copy> Router<'r, K> { 'r: 'f, { if let Some(node_rc) = self.leaf_nodes_rc.get(&route_key) { - let parameter_values: Vec<_> = node_rc.0 + let parameter_values: Vec<_> = node_rc + .0 .borrow() .route_parameter_names .iter() @@ -118,7 +110,7 @@ impl<'r, K: Eq + Hash + Copy> Router<'r, K> { .map(|parameter_value| (self.parameter_value_encoder)(parameter_value)) .collect(); - Some(route_node_stringify(node_rc.clone(), parameter_values)) + Some(node_rc.stringify(parameter_values)) } else { None } From 1199a4611a0cd291644c49259c8d2d5eaaca3538 Mon Sep 17 00:00:00 2001 From: Elmer Bulthuis Date: Sat, 13 Jul 2024 17:51:33 +0200 Subject: [PATCH 5/8] wip --- packages/cargo/goodrouter/src/route_node.rs | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/cargo/goodrouter/src/route_node.rs b/packages/cargo/goodrouter/src/route_node.rs index ea4d80a..ed1c07b 100644 --- a/packages/cargo/goodrouter/src/route_node.rs +++ b/packages/cargo/goodrouter/src/route_node.rs @@ -2,13 +2,13 @@ pub mod route_node_merge; pub mod route_node_rc; pub mod route_node_utility; -use std::{cell::RefCell, cmp::Ordering, collections::BTreeSet, rc}; +use std::{cell, cmp, collections::BTreeSet, rc}; #[derive(Debug)] -pub struct RouteNodeRc<'r, K>(pub rc::Rc>>); +pub struct RouteNodeRc<'r, K>(pub rc::Rc>>); #[derive(Debug)] -pub struct RouteNodeWeak<'r, K>(pub rc::Weak>>); +pub struct RouteNodeWeak<'r, K>(pub rc::Weak>>); #[derive(Debug)] pub struct RouteNode<'r, K> { @@ -36,7 +36,7 @@ impl<'r, K> TryFrom<&RouteNodeWeak<'r, K>> for RouteNodeRc<'r, K> { impl<'r, K> From> for RouteNodeRc<'r, K> { fn from(value: RouteNode<'r, K>) -> Self { - Self(rc::Rc::new(RefCell::new(value))) + Self(rc::Rc::new(cell::RefCell::new(value))) } } @@ -47,46 +47,46 @@ impl<'r, K> From<&RouteNodeRc<'r, K>> for RouteNodeWeak<'r, K> { } impl<'r, K> Ord for RouteNode<'r, K> { - fn cmp(&self, other: &Self) -> Ordering { + fn cmp(&self, other: &Self) -> cmp::Ordering { if self.anchor.len() < other.anchor.len() { - return Ordering::Greater; + return cmp::Ordering::Greater; } if self.anchor.len() > other.anchor.len() { - return Ordering::Less; + return cmp::Ordering::Less; } if !self.has_parameter && other.has_parameter { - return Ordering::Less; + return cmp::Ordering::Less; } if self.has_parameter && !other.has_parameter { - return Ordering::Greater; + return cmp::Ordering::Greater; } if self.anchor < other.anchor { - return Ordering::Less; + return cmp::Ordering::Less; } if self.anchor > other.anchor { - return Ordering::Greater; + return cmp::Ordering::Greater; } - Ordering::Equal + cmp::Ordering::Equal } } impl<'r, K> PartialOrd for RouteNode<'r, K> { - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl<'r, K> Ord for RouteNodeRc<'r, K> { - fn cmp(&self, other: &Self) -> Ordering { + fn cmp(&self, other: &Self) -> cmp::Ordering { self.0.cmp(&other.0) } } impl<'r, K> PartialOrd for RouteNodeRc<'r, K> { - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } From c05e2ce1e11f6fb49c715b563e3082fc1d620521 Mon Sep 17 00:00:00 2001 From: Elmer Bulthuis Date: Sat, 13 Jul 2024 18:07:37 +0200 Subject: [PATCH 6/8] more refs --- .../src/route_node/route_node_merge.rs | 38 +++++++++---------- .../src/route_node/route_node_rc.rs | 4 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/cargo/goodrouter/src/route_node/route_node_merge.rs b/packages/cargo/goodrouter/src/route_node/route_node_merge.rs index d5afe47..2e2e6c3 100644 --- a/packages/cargo/goodrouter/src/route_node/route_node_merge.rs +++ b/packages/cargo/goodrouter/src/route_node/route_node_merge.rs @@ -1,8 +1,8 @@ use super::*; pub fn route_node_merge<'r, K>( - parent_node_rc: RouteNodeRc<'r, K>, - child_node_rc: Option>, + parent_node_rc: &RouteNodeRc<'r, K>, + child_node_rc: Option<&RouteNodeRc<'r, K>>, anchor: &'r str, has_parameter: bool, route_key: Option, @@ -58,7 +58,7 @@ pub fn route_node_merge<'r, K>( } fn route_node_merge_new<'r, K>( - parent_node_rc: RouteNodeRc<'r, K>, + parent_node_rc: &RouteNodeRc<'r, K>, anchor: &'r str, has_parameter: bool, route_key: Option, @@ -69,7 +69,7 @@ fn route_node_merge_new<'r, K>( has_parameter, route_key, route_parameter_names, - parent: Some((&parent_node_rc).into()), + parent: Some(parent_node_rc.into()), ..Default::default() }; @@ -81,7 +81,7 @@ fn route_node_merge_new<'r, K>( } fn route_node_merge_join<'r, K>( - child_node_rc: RouteNodeRc<'r, K>, + child_node_rc: &RouteNodeRc<'r, K>, route_key: Option, route_parameter_names: Vec<&'r str>, ) -> RouteNodeRc<'r, K> { @@ -100,8 +100,8 @@ fn route_node_merge_join<'r, K>( } fn route_node_merge_intermediate<'r, K>( - parent_node_rc: RouteNodeRc<'r, K>, - child_node_rc: RouteNodeRc<'r, K>, + parent_node_rc: &RouteNodeRc<'r, K>, + child_node_rc: &RouteNodeRc<'r, K>, anchor: &'r str, has_parameter: bool, route_key: Option, @@ -121,7 +121,7 @@ fn route_node_merge_intermediate<'r, K>( // remove the child from parent { let mut parent_node = parent_node_rc.0.borrow_mut(); - parent_node.children.remove(&child_node_rc); + parent_node.children.remove(child_node_rc); } // create an intermediate node @@ -131,7 +131,7 @@ fn route_node_merge_intermediate<'r, K>( let mut intermediate_node = RouteNode { anchor: &child_node.anchor[..common_prefix_length], has_parameter: child_node.has_parameter, - parent: Some((&parent_node_rc).into()), + parent: Some(parent_node_rc.into()), ..Default::default() }; @@ -166,8 +166,8 @@ fn route_node_merge_intermediate<'r, K>( } fn route_node_merge_add_to_child<'r, K>( - _parent_node_rc: RouteNodeRc<'r, K>, - child_node_rc: RouteNodeRc<'r, K>, + _parent_node_rc: &RouteNodeRc<'r, K>, + child_node_rc: &RouteNodeRc<'r, K>, anchor: &'r str, _has_parameter: bool, route_key: Option, @@ -177,25 +177,25 @@ fn route_node_merge_add_to_child<'r, K>( let anchor = &anchor[common_prefix_length..]; let has_parameter = false; - let (common_prefix_length2, child_node_rc2) = child_node_rc + let (common_prefix_length_similar, child_node_rc_similar) = child_node_rc .0 .borrow() .find_similar_child(anchor, has_parameter); return route_node_merge( - child_node_rc.clone(), - child_node_rc2, + child_node_rc, + child_node_rc_similar.as_ref(), anchor, has_parameter, route_key, route_parameter_names, - common_prefix_length2, + common_prefix_length_similar, ); } fn route_node_merge_add_to_new<'r, K>( - parent_node_rc: RouteNodeRc<'r, K>, - child_node_rc: RouteNodeRc<'r, K>, + parent_node_rc: &RouteNodeRc<'r, K>, + child_node_rc: &RouteNodeRc<'r, K>, anchor: &'r str, has_parameter: bool, route_key: Option, @@ -213,12 +213,12 @@ fn route_node_merge_add_to_new<'r, K>( let mut parent_node = parent_node_rc.0.borrow_mut(); - parent_node.children.remove(&child_node_rc); + parent_node.children.remove(child_node_rc); parent_node.children.insert(new_node_rc.clone()); let mut new_node = new_node_rc.0.borrow_mut(); new_node.children.insert(child_node_rc.clone()); - new_node.parent = Some((&parent_node_rc).into()); + new_node.parent = Some(parent_node_rc.into()); let mut child_node = child_node_rc.0.borrow_mut(); child_node.anchor = &child_node.anchor[common_prefix_length..]; diff --git a/packages/cargo/goodrouter/src/route_node/route_node_rc.rs b/packages/cargo/goodrouter/src/route_node/route_node_rc.rs index e6c5f2e..42c5dea 100644 --- a/packages/cargo/goodrouter/src/route_node/route_node_rc.rs +++ b/packages/cargo/goodrouter/src/route_node/route_node_rc.rs @@ -146,8 +146,8 @@ impl<'r, K> RouteNodeRc<'r, K> { .find_similar_child(anchor, has_parameter); node_current_rc = route_node_merge( - node_current_rc, - child_node_rc, + &node_current_rc, + child_node_rc.as_ref(), anchor, has_parameter, route_key, From 973c8a3fb8e4c732a84793b92fd0961922b728c7 Mon Sep 17 00:00:00 2001 From: Elmer Bulthuis Date: Sat, 13 Jul 2024 18:27:39 +0200 Subject: [PATCH 7/8] reorganize --- packages/cargo/goodrouter/src/route_node.rs | 156 +----------------- .../{route_node_rc.rs => functions.rs} | 33 +++- .../{route_node_merge.rs => merge.rs} | 99 +++++------ .../src/route_node/route_node_utility.rs | 30 ---- .../cargo/goodrouter/src/route_node/traits.rs | 150 +++++++++++++++++ 5 files changed, 234 insertions(+), 234 deletions(-) rename packages/cargo/goodrouter/src/route_node/{route_node_rc.rs => functions.rs} (86%) rename packages/cargo/goodrouter/src/route_node/{route_node_merge.rs => merge.rs} (76%) delete mode 100644 packages/cargo/goodrouter/src/route_node/route_node_utility.rs create mode 100644 packages/cargo/goodrouter/src/route_node/traits.rs diff --git a/packages/cargo/goodrouter/src/route_node.rs b/packages/cargo/goodrouter/src/route_node.rs index ed1c07b..e39dbef 100644 --- a/packages/cargo/goodrouter/src/route_node.rs +++ b/packages/cargo/goodrouter/src/route_node.rs @@ -1,8 +1,8 @@ -pub mod route_node_merge; -pub mod route_node_rc; -pub mod route_node_utility; +mod functions; +mod merge; +mod traits; -use std::{cell, cmp, collections::BTreeSet, rc}; +use std::{cell, collections::BTreeSet, rc}; #[derive(Debug)] pub struct RouteNodeRc<'r, K>(pub rc::Rc>>); @@ -25,151 +25,3 @@ pub struct RouteNode<'r, K> { // parent node, should only be null for the root node parent: Option>, } - -impl<'r, K> TryFrom<&RouteNodeWeak<'r, K>> for RouteNodeRc<'r, K> { - type Error = (); - - fn try_from(value: &RouteNodeWeak<'r, K>) -> Result { - Ok(Self(value.0.upgrade().ok_or(())?)) - } -} - -impl<'r, K> From> for RouteNodeRc<'r, K> { - fn from(value: RouteNode<'r, K>) -> Self { - Self(rc::Rc::new(cell::RefCell::new(value))) - } -} - -impl<'r, K> From<&RouteNodeRc<'r, K>> for RouteNodeWeak<'r, K> { - fn from(value: &RouteNodeRc<'r, K>) -> Self { - Self(rc::Rc::downgrade(&value.0)) - } -} - -impl<'r, K> Ord for RouteNode<'r, K> { - fn cmp(&self, other: &Self) -> cmp::Ordering { - if self.anchor.len() < other.anchor.len() { - return cmp::Ordering::Greater; - } - if self.anchor.len() > other.anchor.len() { - return cmp::Ordering::Less; - } - - if !self.has_parameter && other.has_parameter { - return cmp::Ordering::Less; - } - if self.has_parameter && !other.has_parameter { - return cmp::Ordering::Greater; - } - - if self.anchor < other.anchor { - return cmp::Ordering::Less; - } - if self.anchor > other.anchor { - return cmp::Ordering::Greater; - } - - cmp::Ordering::Equal - } -} - -impl<'r, K> PartialOrd for RouteNode<'r, K> { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl<'r, K> Ord for RouteNodeRc<'r, K> { - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.0.cmp(&other.0) - } -} - -impl<'r, K> PartialOrd for RouteNodeRc<'r, K> { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl<'r, K> Eq for RouteNode<'r, K> {} - -impl<'r, K> PartialEq for RouteNode<'r, K> { - fn eq(&self, other: &Self) -> bool { - self.anchor == other.anchor && self.has_parameter == other.has_parameter - } -} - -impl<'r, K> Eq for RouteNodeRc<'r, K> {} - -impl<'r, K> PartialEq for RouteNodeRc<'r, K> { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl<'r, K> Default for RouteNode<'r, K> { - fn default() -> Self { - Self { - route_key: None, - route_parameter_names: Default::default(), - anchor: Default::default(), - has_parameter: Default::default(), - children: Default::default(), - parent: Default::default(), - } - } -} - -impl<'r, K> Default for RouteNodeRc<'r, K> { - fn default() -> Self { - Self(Default::default()) - } -} - -impl<'r, K> Clone for RouteNodeRc<'r, K> { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use itertools::Itertools; - use std::iter::FromIterator; - - #[test] - fn route_ordering() { - let nodes = vec![ - RouteNode { - route_key: None, - has_parameter: false, - anchor: "aa", - ..Default::default() - }, - RouteNode { - route_key: Some(&1), - has_parameter: false, - anchor: "xx", - ..Default::default() - }, - RouteNode { - route_key: None, - has_parameter: true, - anchor: "aa", - ..Default::default() - }, - RouteNode { - route_key: None, - has_parameter: false, - anchor: "x", - ..Default::default() - }, - ]; - - let nodes_expected = nodes.iter(); - let nodes_actual = nodes.iter().sorted(); - - assert_eq!(Vec::from_iter(nodes_actual), Vec::from_iter(nodes_expected)); - } -} diff --git a/packages/cargo/goodrouter/src/route_node/route_node_rc.rs b/packages/cargo/goodrouter/src/route_node/functions.rs similarity index 86% rename from packages/cargo/goodrouter/src/route_node/route_node_rc.rs rename to packages/cargo/goodrouter/src/route_node/functions.rs index 42c5dea..e858154 100644 --- a/packages/cargo/goodrouter/src/route_node/route_node_rc.rs +++ b/packages/cargo/goodrouter/src/route_node/functions.rs @@ -1,5 +1,5 @@ -use super::route_node_merge::*; use super::*; +use crate::string_utility::find_common_prefix_length; use crate::template::template_pairs::parse_template_pairs; use regex::Regex; use std::borrow::Cow; @@ -145,8 +145,7 @@ impl<'r, K> RouteNodeRc<'r, K> { .borrow() .find_similar_child(anchor, has_parameter); - node_current_rc = route_node_merge( - &node_current_rc, + node_current_rc = node_current_rc.merge( child_node_rc.as_ref(), anchor, has_parameter, @@ -160,6 +159,34 @@ impl<'r, K> RouteNodeRc<'r, K> { } } +impl<'r, K> RouteNode<'r, K> { + pub fn find_similar_child( + &self, + anchor: &'r str, + has_parameter: bool, + ) -> (usize, Option>) { + let anchor_chars: Vec<_> = anchor.chars().collect(); + + for child_node_rc in self.children.iter() { + if child_node_rc.0.borrow().has_parameter != has_parameter { + continue; + } + + let child_anchor_chars: Vec<_> = child_node_rc.0.borrow().anchor.chars().collect(); + + let common_prefix_length = find_common_prefix_length(&anchor_chars, &child_anchor_chars); + + if common_prefix_length == 0 { + continue; + } + + return (common_prefix_length, Some(child_node_rc.clone())); + } + + Default::default() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/packages/cargo/goodrouter/src/route_node/route_node_merge.rs b/packages/cargo/goodrouter/src/route_node/merge.rs similarity index 76% rename from packages/cargo/goodrouter/src/route_node/route_node_merge.rs rename to packages/cargo/goodrouter/src/route_node/merge.rs index 2e2e6c3..fbddc30 100644 --- a/packages/cargo/goodrouter/src/route_node/route_node_merge.rs +++ b/packages/cargo/goodrouter/src/route_node/merge.rs @@ -1,59 +1,61 @@ use super::*; -pub fn route_node_merge<'r, K>( - parent_node_rc: &RouteNodeRc<'r, K>, - child_node_rc: Option<&RouteNodeRc<'r, K>>, - anchor: &'r str, - has_parameter: bool, - route_key: Option, - route_parameter_names: Vec<&'r str>, - common_prefix_length: usize, -) -> RouteNodeRc<'r, K> { - if let Some(child_node_rc) = child_node_rc { - let common_prefix = &anchor[..common_prefix_length]; - let child_anchor = child_node_rc.0.borrow().anchor; - - if child_anchor == anchor { - return route_node_merge_join(child_node_rc, route_key, route_parameter_names.clone()); - } else if child_anchor == common_prefix { - return route_node_merge_add_to_child( - parent_node_rc, - child_node_rc, - anchor, - has_parameter, - route_key, - route_parameter_names.clone(), - common_prefix_length, - ); - } else if anchor == common_prefix { - return route_node_merge_add_to_new( - parent_node_rc, - child_node_rc, - anchor, - has_parameter, - route_key, - route_parameter_names.clone(), - common_prefix_length, - ); +impl<'r, K> RouteNodeRc<'r, K> { + pub fn merge( + &self, + child_node_rc: Option<&RouteNodeRc<'r, K>>, + anchor: &'r str, + has_parameter: bool, + route_key: Option, + route_parameter_names: Vec<&'r str>, + common_prefix_length: usize, + ) -> RouteNodeRc<'r, K> { + if let Some(child_node_rc) = child_node_rc { + let common_prefix = &anchor[..common_prefix_length]; + let child_anchor = child_node_rc.0.borrow().anchor; + + if child_anchor == anchor { + return route_node_merge_join(child_node_rc, route_key, route_parameter_names.clone()); + } else if child_anchor == common_prefix { + return route_node_merge_add_to_child( + self, + child_node_rc, + anchor, + has_parameter, + route_key, + route_parameter_names.clone(), + common_prefix_length, + ); + } else if anchor == common_prefix { + return route_node_merge_add_to_new( + self, + child_node_rc, + anchor, + has_parameter, + route_key, + route_parameter_names.clone(), + common_prefix_length, + ); + } else { + return route_node_merge_intermediate( + self, + child_node_rc, + anchor, + has_parameter, + route_key, + route_parameter_names.clone(), + common_prefix_length, + ); + } } else { - return route_node_merge_intermediate( - parent_node_rc, - child_node_rc, + return route_node_merge_new( + self, anchor, has_parameter, route_key, route_parameter_names.clone(), - common_prefix_length, ); } - } else { - return route_node_merge_new( - parent_node_rc, - anchor, - has_parameter, - route_key, - route_parameter_names.clone(), - ); } } @@ -182,8 +184,7 @@ fn route_node_merge_add_to_child<'r, K>( .borrow() .find_similar_child(anchor, has_parameter); - return route_node_merge( - child_node_rc, + return child_node_rc.merge( child_node_rc_similar.as_ref(), anchor, has_parameter, diff --git a/packages/cargo/goodrouter/src/route_node/route_node_utility.rs b/packages/cargo/goodrouter/src/route_node/route_node_utility.rs deleted file mode 100644 index 22f9c3f..0000000 --- a/packages/cargo/goodrouter/src/route_node/route_node_utility.rs +++ /dev/null @@ -1,30 +0,0 @@ -use super::*; -use crate::string_utility::find_common_prefix_length; - -impl<'r, K> RouteNode<'r, K> { - pub fn find_similar_child( - &self, - anchor: &'r str, - has_parameter: bool, - ) -> (usize, Option>) { - let anchor_chars: Vec<_> = anchor.chars().collect(); - - for child_node_rc in self.children.iter() { - if child_node_rc.0.borrow().has_parameter != has_parameter { - continue; - } - - let child_anchor_chars: Vec<_> = child_node_rc.0.borrow().anchor.chars().collect(); - - let common_prefix_length = find_common_prefix_length(&anchor_chars, &child_anchor_chars); - - if common_prefix_length == 0 { - continue; - } - - return (common_prefix_length, Some(child_node_rc.clone())); - } - - Default::default() - } -} diff --git a/packages/cargo/goodrouter/src/route_node/traits.rs b/packages/cargo/goodrouter/src/route_node/traits.rs new file mode 100644 index 0000000..3d65ad2 --- /dev/null +++ b/packages/cargo/goodrouter/src/route_node/traits.rs @@ -0,0 +1,150 @@ +use super::*; +use std::{cell, cmp, rc}; + +impl<'r, K> TryFrom<&RouteNodeWeak<'r, K>> for RouteNodeRc<'r, K> { + type Error = (); + + fn try_from(value: &RouteNodeWeak<'r, K>) -> Result { + Ok(Self(value.0.upgrade().ok_or(())?)) + } +} + +impl<'r, K> From> for RouteNodeRc<'r, K> { + fn from(value: RouteNode<'r, K>) -> Self { + Self(rc::Rc::new(cell::RefCell::new(value))) + } +} + +impl<'r, K> From<&RouteNodeRc<'r, K>> for RouteNodeWeak<'r, K> { + fn from(value: &RouteNodeRc<'r, K>) -> Self { + Self(rc::Rc::downgrade(&value.0)) + } +} + +impl<'r, K> Ord for RouteNode<'r, K> { + fn cmp(&self, other: &Self) -> cmp::Ordering { + if self.anchor.len() < other.anchor.len() { + return cmp::Ordering::Greater; + } + if self.anchor.len() > other.anchor.len() { + return cmp::Ordering::Less; + } + + if !self.has_parameter && other.has_parameter { + return cmp::Ordering::Less; + } + if self.has_parameter && !other.has_parameter { + return cmp::Ordering::Greater; + } + + if self.anchor < other.anchor { + return cmp::Ordering::Less; + } + if self.anchor > other.anchor { + return cmp::Ordering::Greater; + } + + cmp::Ordering::Equal + } +} + +impl<'r, K> PartialOrd for RouteNode<'r, K> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl<'r, K> Ord for RouteNodeRc<'r, K> { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.0.cmp(&other.0) + } +} + +impl<'r, K> PartialOrd for RouteNodeRc<'r, K> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl<'r, K> Eq for RouteNode<'r, K> {} + +impl<'r, K> PartialEq for RouteNode<'r, K> { + fn eq(&self, other: &Self) -> bool { + self.anchor == other.anchor && self.has_parameter == other.has_parameter + } +} + +impl<'r, K> Eq for RouteNodeRc<'r, K> {} + +impl<'r, K> PartialEq for RouteNodeRc<'r, K> { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl<'r, K> Default for RouteNode<'r, K> { + fn default() -> Self { + Self { + route_key: None, + route_parameter_names: Default::default(), + anchor: Default::default(), + has_parameter: Default::default(), + children: Default::default(), + parent: Default::default(), + } + } +} + +impl<'r, K> Default for RouteNodeRc<'r, K> { + fn default() -> Self { + Self(Default::default()) + } +} + +impl<'r, K> Clone for RouteNodeRc<'r, K> { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use itertools::Itertools; + use std::iter::FromIterator; + + #[test] + fn route_ordering() { + let nodes = vec![ + RouteNode { + route_key: None, + has_parameter: false, + anchor: "aa", + ..Default::default() + }, + RouteNode { + route_key: Some(&1), + has_parameter: false, + anchor: "xx", + ..Default::default() + }, + RouteNode { + route_key: None, + has_parameter: true, + anchor: "aa", + ..Default::default() + }, + RouteNode { + route_key: None, + has_parameter: false, + anchor: "x", + ..Default::default() + }, + ]; + + let nodes_expected = nodes.iter(); + let nodes_actual = nodes.iter().sorted(); + + assert_eq!(Vec::from_iter(nodes_actual), Vec::from_iter(nodes_expected)); + } +} From cca3f0abe9f9629922b37a0ea3911ce337d3634f Mon Sep 17 00:00:00 2001 From: Elmer Bulthuis Date: Sat, 13 Jul 2024 18:57:49 +0200 Subject: [PATCH 8/8] format --- packages/cargo/goodrouter/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cargo/goodrouter/src/lib.rs b/packages/cargo/goodrouter/src/lib.rs index 6e8dfa2..635acd5 100644 --- a/packages/cargo/goodrouter/src/lib.rs +++ b/packages/cargo/goodrouter/src/lib.rs @@ -1,4 +1,5 @@ mod route_node; -pub mod router; mod string_utility; mod template; + +pub mod router;