From 87804fe7a3d66e8c5811c87bf108fcd6a8401662 Mon Sep 17 00:00:00 2001 From: Changyuan Lyu Date: Tue, 24 Mar 2026 17:41:31 -0700 Subject: [PATCH 1/2] Add tests for trailing slash prefix-suffix conflict bug Add test cases to unveil an issue where the order of adding routes like /x20/f{a}o/{*path} and /x20/f{a}o could result in a conflict. Signed-off-by: Changyuan Lyu --- tests/insert.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/insert.rs b/tests/insert.rs index 44864d4..fb8f327 100644 --- a/tests/insert.rs +++ b/tests/insert.rs @@ -257,6 +257,10 @@ fn prefix_suffix_conflict() { ("/x16/prefix{a}suffix", Ok(())), ("/x17/prefix{a}/z", Ok(())), ("/x18/prefix{a}/z", Ok(())), + ("/x19/f{a}o", Ok(())), + ("/x19/f{a}o/{*path}", Ok(())), + ("/x20/f{a}o/{*path}", Ok(())), + ("/x20/f{a}o", Err(conflict("/x20/f{a}o/{*path}"))), ]) .run() } From 287087cec90d03fe261116d89cf17d11b6388a75 Mon Sep 17 00:00:00 2001 From: Changyuan Lyu Date: Tue, 24 Mar 2026 17:39:46 -0700 Subject: [PATCH 2/2] Fix prefix-suffix conflict due to insertion order of trailing slashes When inserting route parameters with static suffixes, `matchit` checked for conflicts between newly inserted suffixes and existing ones. It had an explicit exception to allow a new suffix that only differed by an extra trailing slash (e.g., `o/` vs existing `o`). However, this check only evaluated one direction: if the *new* suffix was longer than the *existing* suffix. If the routes were inserted in the reverse order (e.g., `o/` inserted first, then `o` inserted second), the router incorrectly flagged them as a prefix-suffix conflict. This commit makes the trailing slash check bidirectional by adding an `else` branch to verify if the *existing* suffix is the one with the extra trailing slash, resolving the insertion order dependency. Signed-off-by: Changyuan Lyu --- src/tree.rs | 5 +++++ tests/insert.rs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tree.rs b/src/tree.rs index 4d4695b..f7c9a2d 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -223,6 +223,11 @@ impl Node { if *common == *child.prefix && remaining == *b"/" { extra_trailing_slash = true; } + } else { + let (common, remaining) = child.prefix.split_at(suffix.len()); + if *common == **suffix && remaining == *b"/" { + extra_trailing_slash = true; + } } } diff --git a/tests/insert.rs b/tests/insert.rs index fb8f327..0587fdb 100644 --- a/tests/insert.rs +++ b/tests/insert.rs @@ -260,7 +260,7 @@ fn prefix_suffix_conflict() { ("/x19/f{a}o", Ok(())), ("/x19/f{a}o/{*path}", Ok(())), ("/x20/f{a}o/{*path}", Ok(())), - ("/x20/f{a}o", Err(conflict("/x20/f{a}o/{*path}"))), + ("/x20/f{a}o", Ok(())), ]) .run() }