From bed678c0a64c813d3566a9525d7c7376aac40e47 Mon Sep 17 00:00:00 2001 From: Troy Benson Date: Mon, 1 Jan 2024 18:50:42 +0000 Subject: [PATCH 1/5] feat: normal match repeated pattern This commit adds a feature which allows the normal matcher to match paths like `/:file.gz` or `/:lib.js.gz` Previously this would have found no routes on paths like `/node.js.gz` Now `/:file.gz` matches `/node.js.gz` where "name" => "node.js" And `/:lib.js.gz` matches `/node.js.gz` where "lib" => "node" Which to me seems like the correct result in these cases. fixes: #36 --- src/node.rs | 31 ++++++++++++++++++++++++++----- tests/tree.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/src/node.rs b/src/node.rs index b6131c3..d080e3d 100644 --- a/src/node.rs +++ b/src/node.rs @@ -285,12 +285,33 @@ impl Node { if let Some(id) = self.nodes0.as_ref().and_then(|nodes| { nodes.iter().find_map(|node| match &node.key { Key::String(s) => { - bytes.iter().position(|b| s[0] == *b).and_then(|n| { - node._find(start + n, &bytes[n..], ranges).map(|id| { - ranges.push(start..start + n); - id + let mut keep_running = true; + bytes + .iter() + // as it turns out doing .copied() here is much slower than dereferencing in the closure + // https://godbolt.org/z/7dnW91T1Y + .take_while(|b| { + if **b == b'/' && keep_running { + keep_running = false; + true + } else { + keep_running + } + }) + .enumerate() + .filter_map( + |(idx, b)| if s[0] == *b { Some(idx) } else { None }, + ) + .find_map(|n| { + let found = node + ._find(start + n, &bytes[n..], ranges) + .map(|id| { + ranges.push(start..start + n); + id + }); + + found }) - }) } Key::Parameter(_) => unreachable!(), }) diff --git a/tests/tree.rs b/tests/tree.rs index 16d8fc7..2850e20 100644 --- a/tests/tree.rs +++ b/tests/tree.rs @@ -2257,3 +2257,50 @@ fn test_dots_ext() { assert_eq!(params.params(), &[("name", "abc.xyz")]); } + +#[test] +fn test_dots_ext_no_qualifier() { + let mut tree = PathTree::new(); + let _ = tree.insert("/:name.js", 2); + let _ = tree.insert("/:name.js.gz", 1); + + let result = tree.find("/node.js"); + assert!(result.is_some()); + + let (value, params) = result.unwrap(); + assert_eq!(value, &2); + + assert_eq!(params.params(), &[("name", "node")]); + + let result = tree.find("/path.lib.js"); + assert!(result.is_some()); + + let (value, params) = result.unwrap(); + assert_eq!(value, &2); + + assert_eq!(params.params(), &[("name", "path.lib")]); + + let result = tree.find("/node.js.js"); + assert!(result.is_some()); + + let (value, params) = result.unwrap(); + assert_eq!(value, &2); + + assert_eq!(params.params(), &[("name", "node.js")]); + + let result = tree.find("/node.js.gz"); + assert!(result.is_some()); + + let (value, params) = result.unwrap(); + assert_eq!(value, &1); + + assert_eq!(params.params(), &[("name", "node")]); + + let result = tree.find("/node.js.gz.js.gz"); + assert!(result.is_some()); + + let (value, params) = result.unwrap(); + assert_eq!(value, &1); + + assert_eq!(params.params(), &[("name", "node.js.gz")]); +} From ee19d34d0828445479375c4ae3b140a7ea001324 Mon Sep 17 00:00:00 2001 From: Fangdun Tsai Date: Tue, 2 Jan 2024 12:42:15 +0800 Subject: [PATCH 2/5] chore: remove iter copied --- src/node.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/node.rs b/src/node.rs index d080e3d..f786025 100644 --- a/src/node.rs +++ b/src/node.rs @@ -417,20 +417,16 @@ impl Node { m >= s.len() }; if right_length { - return bytes - .iter() - .enumerate() - .filter_map( - |(n, b)| if s[0] == *b { Some(n) } else { None }, - ) - .find_map(|n| { + return bytes.iter().position(|b| s[0] == *b).and_then( + |n| { node._find(start + n, &bytes[n..], ranges).map( |id| { ranges.push(start..start + n); id }, ) - }); + }, + ); } } None From 513ce7418485932af79c2aa896c77fc0fe00aee8 Mon Sep 17 00:00:00 2001 From: Fangdun Tsai Date: Tue, 2 Jan 2024 12:59:52 +0800 Subject: [PATCH 3/5] fix: revert iter enumerate --- src/node.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/node.rs b/src/node.rs index f786025..a5a2930 100644 --- a/src/node.rs +++ b/src/node.rs @@ -417,16 +417,15 @@ impl Node { m >= s.len() }; if right_length { - return bytes.iter().position(|b| s[0] == *b).and_then( - |n| { - node._find(start + n, &bytes[n..], ranges).map( - |id| { - ranges.push(start..start + n); - id - }, - ) - }, - ); + return bytes.iter().enumerate().find_map(|(n, b)| { + if s[0] != *b { + return None; + } + node._find(start + n, &bytes[n..], ranges).map(|id| { + ranges.push(start..start + n); + id + }) + }); } } None From 90676a6e004937196bd37a357603af3611f035bf Mon Sep 17 00:00:00 2001 From: Fangdun Tsai Date: Tue, 2 Jan 2024 13:03:34 +0800 Subject: [PATCH 4/5] chore: remove unless comments --- src/node.rs | 70 ----------------------------------------------------- 1 file changed, 70 deletions(-) diff --git a/src/node.rs b/src/node.rs index a5a2930..a5b64d8 100644 --- a/src/node.rs +++ b/src/node.rs @@ -197,76 +197,6 @@ impl Node { } } Key::Parameter(k) => match k { - // Kind::Normal => { - // if m == 0 { - // return None; - // } - // - // // slash node - // let mut slash = None; - // // slash position at path - // let sp = bytes.iter().position(|b| *b == b'/'); - // - // // static - // if let Some(id) = self.nodes0.as_ref().and_then(|nodes| { - // let tmp = &bytes[0..sp.unwrap_or(m)]; - // nodes.iter().find_map(|node| match node.key { - // Key::String(s) => { - // if s[0] == b'/' { - // slash.replace(node); - // return None; - // } - // - // tmp.iter().position(|b| s[0] == *b).and_then(|n| { - // node._find(start + n, &bytes[n..], ranges).map(|id| { - // ranges.push(start); - // ranges.push(start + n); - // id - // }) - // }) - // } - // _ => unreachable!(), - // }) - // }) { - // return Some(id); - // } - // - // // parameter => `:a:b:c` - // if let Some(id) = self.nodes1.as_ref().and_then(|nodes| { - // let b = m - 1 > 0; - // nodes - // .iter() - // .filter(|node| match node.key { - // Key::Parameter(pk) - // if pk == Kind::Normal || pk == Kind::OneOrMore => - // { - // b - // } - // _ => true, - // }) - // .find_map(|node| node._find(start + 1, &bytes[1..], ranges)) - // }) { - // ranges.push(start); - // ranges.push(start + 1); - // return Some(id); - // } - // - // if let Some(n) = sp { - // return slash - // .and_then(|node| node._find(start + n, &bytes[n..], ranges)) - // .map(|id| { - // ranges.push(start); - // ranges.push(start + n); - // id - // }); - // } else { - // return self.value.as_ref().map(|id| { - // ranges.push(start); - // ranges.push(start + m); - // id - // }); - // } - // } Kind::Normal | Kind::Optional | Kind::OptionalSegment => { if m == 0 { if k == &Kind::Normal { From 5afe4228d61ee7071f8a74ec6db363a8b818ee70 Mon Sep 17 00:00:00 2001 From: Fangdun Tsai Date: Tue, 2 Jan 2024 13:41:17 +0800 Subject: [PATCH 5/5] chore: check keeprunning first --- src/node.rs | 22 +++++++++------------- tests/tree.rs | 10 ++++++++++ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/node.rs b/src/node.rs index a5b64d8..a0320ef 100644 --- a/src/node.rs +++ b/src/node.rs @@ -221,7 +221,7 @@ impl Node { // as it turns out doing .copied() here is much slower than dereferencing in the closure // https://godbolt.org/z/7dnW91T1Y .take_while(|b| { - if **b == b'/' && keep_running { + if keep_running && **b == b'/' { keep_running = false; true } else { @@ -229,18 +229,14 @@ impl Node { } }) .enumerate() - .filter_map( - |(idx, b)| if s[0] == *b { Some(idx) } else { None }, - ) - .find_map(|n| { - let found = node - ._find(start + n, &bytes[n..], ranges) - .map(|id| { - ranges.push(start..start + n); - id - }); - - found + .find_map(|(n, b)| { + if s[0] != *b { + return None; + } + node._find(start + n, &bytes[n..], ranges).map(|id| { + ranges.push(start..start + n); + id + }) }) } Key::Parameter(_) => unreachable!(), diff --git a/tests/tree.rs b/tests/tree.rs index 2850e20..2596381 100644 --- a/tests/tree.rs +++ b/tests/tree.rs @@ -2264,6 +2264,16 @@ fn test_dots_ext_no_qualifier() { let _ = tree.insert("/:name.js", 2); let _ = tree.insert("/:name.js.gz", 1); + assert_eq!( + format!("{:?}", &tree.node), + r" +/ +└── : + └── .js •0 + └── .gz •1 +" + ); + let result = tree.find("/node.js"); assert!(result.is_some());