Skip to content

Commit 65f24ed

Browse files
committed
error on prefix-suffix conflicts
1 parent 654813a commit 65f24ed

2 files changed

Lines changed: 217 additions & 43 deletions

File tree

src/tree.rs

Lines changed: 116 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,40 @@ pub(crate) enum NodeType {
7171
unsafe impl<T: Send> Send for Node<T> {}
7272
unsafe impl<T: Sync> Sync for Node<T> {}
7373

74+
struct InsertState<'node, T> {
75+
parent: &'node mut Node<T>,
76+
child: Option<usize>,
77+
}
78+
79+
impl<'node, T> InsertState<'node, T> {
80+
fn node(&mut self) -> &mut Node<T> {
81+
match self.child {
82+
None => self.parent,
83+
Some(i) => &mut self.parent.children[i],
84+
}
85+
}
86+
87+
fn parent(&mut self) -> Option<&mut Node<T>> {
88+
match self.child {
89+
None => None,
90+
Some(_) => Some(self.parent),
91+
}
92+
}
93+
94+
fn set_child(self, i: usize) -> InsertState<'node, T> {
95+
match self.child {
96+
None => InsertState {
97+
parent: self.parent,
98+
child: Some(i),
99+
},
100+
Some(prev) => InsertState {
101+
parent: &mut self.parent.children[prev],
102+
child: Some(i),
103+
},
104+
}
105+
}
106+
}
107+
74108
impl<T> Node<T> {
75109
// Insert a route into the tree.
76110
pub fn insert(&mut self, route: String, val: T) -> Result<(), InsertError> {
@@ -88,21 +122,27 @@ impl<T> Node<T> {
88122
return Ok(());
89123
}
90124

91-
let mut node = self;
125+
let mut state = InsertState {
126+
parent: self,
127+
child: None,
128+
};
129+
92130
'walk: loop {
93131
// Find the common prefix between the route and the current node.
94-
let len = min(remaining.len(), node.prefix.len());
132+
let len = min(remaining.len(), state.node().prefix.len());
95133
let common_prefix = (0..len)
96134
.find(|&i| {
97-
remaining[i] != node.prefix[i]
135+
remaining[i] != state.node().prefix[i]
98136
// Make sure not confuse the start of a wildcard with an escaped `{`.
99-
|| remaining.is_escaped(i) != node.prefix.is_escaped(i)
137+
|| remaining.is_escaped(i) != state.node().prefix.is_escaped(i)
100138
})
101139
.unwrap_or(len);
102140

103141
// If this node has a longer prefix than we need, we have to fork and extract the
104142
// common prefix into a shared parent.
105-
if node.prefix.len() > common_prefix {
143+
if state.node().prefix.len() > common_prefix {
144+
let node = state.node();
145+
106146
// Move the non-matching suffix into a child node.
107147
let suffix = node.prefix.as_ref().slice_off(common_prefix).to_owned();
108148
let child = Node {
@@ -125,6 +165,8 @@ impl<T> Node<T> {
125165
}
126166

127167
if remaining.len() == common_prefix {
168+
let node = state.node();
169+
128170
// This node must not already contain a value.
129171
if node.value.is_some() {
130172
return Err(InsertError::conflict(&route, remaining, node));
@@ -144,20 +186,40 @@ impl<T> Node<T> {
144186

145187
// For parameters with a suffix, we have to find the matching suffix or
146188
// create a new child node.
147-
if matches!(node.node_type, NodeType::Param { .. }) {
189+
if matches!(state.node().node_type, NodeType::Param { .. }) {
148190
let terminator = remaining
149191
.iter()
150192
.position(|&b| b == b'/')
193+
// Include the '/' in the suffix.
151194
.map(|b| b + 1)
152195
.unwrap_or(remaining.len());
153196

154197
let suffix = remaining.slice_until(terminator);
155198

156-
for (i, child) in node.children.iter().enumerate() {
199+
if !matches!(*suffix, b"" | b"/") {
200+
if let Some(parent) = state.parent() {
201+
for child in &parent.children {
202+
// If there is a static prefix that also leads to this route
203+
// parameter, we have a prefix-suffix conflict.
204+
//
205+
// For example, `/prefix{a}` and `/{a}suffix` are conflicting, as there is no clear
206+
// choice to match against `/prefixsuffix`.
207+
if matches!(child.node_type, NodeType::Static) && child.wild_child {
208+
return Err(InsertError::conflict(
209+
&route,
210+
UnescapedRoute::default().as_ref(), // TODO
211+
child,
212+
));
213+
}
214+
}
215+
}
216+
}
217+
218+
for (i, child) in state.node().children.iter().enumerate() {
157219
// Find a matching suffix.
158220
if *child.prefix == **suffix {
159-
node = &mut node.children[i];
160-
node.priority += 1;
221+
state.node().priority += 1;
222+
state = state.set_child(i);
161223
continue 'walk;
162224
}
163225
}
@@ -168,19 +230,19 @@ impl<T> Node<T> {
168230
}
169231

170232
// If there is no matching suffix, create a new suffix node.
171-
let child = node.add_suffix_child(Node {
233+
let child = state.node().add_suffix_child(Node {
172234
prefix: suffix.to_owned(),
173235
node_type: NodeType::Static,
174236
priority: 1,
175237
..Node::default()
176238
});
177-
node.node_type = NodeType::Param { suffix: true };
178-
node = &mut node.children[child];
239+
state.node().node_type = NodeType::Param { suffix: true };
240+
state = state.set_child(child);
179241

180242
// If this is the final route segment, insert the value.
181243
if terminator == remaining.len() {
182-
node.value = Some(UnsafeCell::new(val));
183-
node.remapping = remapping;
244+
state.node().value = Some(UnsafeCell::new(val));
245+
state.node().remapping = remapping;
184246
return Ok(());
185247
}
186248

@@ -190,40 +252,61 @@ impl<T> Node<T> {
190252

191253
// Create a static node unless we are inserting a parameter.
192254
if remaining[0] != b'{' || remaining.is_escaped(0) {
193-
let child = node.add_child(Node {
255+
let child = state.node().add_child(Node {
194256
node_type: NodeType::Static,
195257
priority: 1,
196258
..Node::default()
197259
});
198-
node.indices.push(remaining[0]);
199-
node = &mut node.children[child];
260+
state.node().indices.push(remaining[0]);
261+
state = state.set_child(child);
200262
}
201263

202264
// Insert the remaining route.
203-
let last = node.insert_route(remaining, val)?;
265+
let last = state.node().insert_route(remaining, val)?;
204266
last.remapping = remapping;
205267
return Ok(());
206268
}
207269

208270
// Find a child node that matches the next character in the route.
209-
for mut i in 0..node.indices.len() {
210-
if next == node.indices[i] {
271+
for mut i in 0..state.node().indices.len() {
272+
if next == state.node().indices[i] {
211273
// Make sure not confuse the start of a wildcard with an escaped `{` or `}`.
212274
if matches!(next, b'{' | b'}') && !remaining.is_escaped(0) {
213275
continue;
214276
}
215277

216278
// Continue searching in the child.
217-
i = node.update_child_priority(i);
218-
node = &mut node.children[i];
279+
i = state.node().update_child_priority(i);
280+
state = state.set_child(i);
219281
continue 'walk;
220282
}
221283
}
222284

223285
// We couldn't find a matching child.
224286
//
225287
// If we're not inserting a wildcard we have to create a static child.
226-
if (next != b'{' || remaining.is_escaped(0)) && node.node_type != NodeType::CatchAll {
288+
if (next != b'{' || remaining.is_escaped(0))
289+
&& state.node().node_type != NodeType::CatchAll
290+
{
291+
let node = state.node();
292+
293+
let terminator = remaining
294+
.iter()
295+
.position(|&b| b == b'/')
296+
.unwrap_or(remaining.len());
297+
298+
if let Ok(Some(wildcard)) = find_wildcard(remaining.slice_until(terminator)) {
299+
// If there is a parameter prefix and this node also has a parameter suffix, we
300+
// have a prefix-suffix conflict.
301+
if wildcard.start > 0 && node.wild_child {
302+
let wild_child = node.children.last().unwrap();
303+
304+
if matches!(wild_child.node_type, NodeType::Param { suffix: true }) {
305+
return Err(InsertError::conflict(&route, remaining, wild_child));
306+
}
307+
}
308+
}
309+
227310
node.indices.push(next);
228311
let child = node.add_child(Node::default());
229312
let child = node.update_child_priority(child);
@@ -237,29 +320,30 @@ impl<T> Node<T> {
237320
// We're trying to insert a wildcard.
238321
//
239322
// If this node already has a wildcard child, we have to make sure it matches.
240-
if node.wild_child {
323+
if state.node().wild_child {
241324
// Wildcards are always the last child.
242-
node = node.children.last_mut().unwrap();
243-
node.priority += 1;
325+
let wild_child = state.node().children.len() - 1;
326+
state = state.set_child(wild_child);
327+
state.node().priority += 1;
244328

245329
// Make sure the route parameter matches.
246-
if let Some(wildcard) = remaining.get(..node.prefix.len()) {
247-
if *wildcard != *node.prefix {
248-
return Err(InsertError::conflict(&route, remaining, node));
330+
if let Some(wildcard) = remaining.get(..state.node().prefix.len()) {
331+
if *wildcard != *state.node().prefix {
332+
return Err(InsertError::conflict(&route, remaining, state.node()));
249333
}
250334
}
251335

252336
// Catch-all routes cannot have children.
253-
if node.node_type == NodeType::CatchAll {
254-
return Err(InsertError::conflict(&route, remaining, node));
337+
if state.node().node_type == NodeType::CatchAll {
338+
return Err(InsertError::conflict(&route, remaining, state.node()));
255339
}
256340

257341
// Continue with the wildcard node.
258342
continue 'walk;
259343
}
260344

261345
// Otherwise, create a new node for the wildcard and insert the route.
262-
let last = node.insert_route(remaining, val)?;
346+
let last = state.node().insert_route(remaining, val)?;
263347
last.remapping = remapping;
264348
return Ok(());
265349
}

0 commit comments

Comments
 (0)