Skip to content

Commit ae83380

Browse files
committed
wip
1 parent 65f24ed commit ae83380

4 files changed

Lines changed: 334 additions & 140 deletions

File tree

src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl InsertError {
7979
route.append(&current.prefix);
8080
}
8181

82-
// Add the prefixes of any conflicting children.
82+
// Add the prefixes of the first conflicting child.
8383
let mut child = current.children.first();
8484
while let Some(node) = child {
8585
route.append(&node.prefix);

src/tree.rs

Lines changed: 174 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub struct Node<T> {
3434
// The value stored at this node.
3535
//
3636
// See `Node::at` for why an `UnsafeCell` is necessary.
37-
value: Option<UnsafeCell<T>>,
37+
pub(crate) value: Option<UnsafeCell<T>>,
3838

3939
// A parameter name remapping, stored at nodes that hold values.
4040
pub(crate) remapping: ParamRemapping,
@@ -77,20 +77,34 @@ struct InsertState<'node, T> {
7777
}
7878

7979
impl<'node, T> InsertState<'node, T> {
80-
fn node(&mut self) -> &mut Node<T> {
80+
fn node_mut(&mut self) -> &mut Node<T> {
8181
match self.child {
8282
None => self.parent,
8383
Some(i) => &mut self.parent.children[i],
8484
}
8585
}
8686

87-
fn parent(&mut self) -> Option<&mut Node<T>> {
87+
fn parent_mut(&mut self) -> Option<&mut Node<T>> {
8888
match self.child {
8989
None => None,
9090
Some(_) => Some(self.parent),
9191
}
9292
}
9393

94+
fn parent(&self) -> Option<&Node<T>> {
95+
match self.child {
96+
None => None,
97+
Some(_) => Some(self.parent),
98+
}
99+
}
100+
101+
fn node(&self) -> &Node<T> {
102+
match self.child {
103+
None => self.parent,
104+
Some(i) => &self.parent.children[i],
105+
}
106+
}
107+
94108
fn set_child(self, i: usize) -> InsertState<'node, T> {
95109
match self.child {
96110
None => InsertState {
@@ -129,19 +143,19 @@ impl<T> Node<T> {
129143

130144
'walk: loop {
131145
// Find the common prefix between the route and the current node.
132-
let len = min(remaining.len(), state.node().prefix.len());
146+
let len = min(remaining.len(), state.node_mut().prefix.len());
133147
let common_prefix = (0..len)
134148
.find(|&i| {
135-
remaining[i] != state.node().prefix[i]
149+
remaining[i] != state.node_mut().prefix[i]
136150
// Make sure not confuse the start of a wildcard with an escaped `{`.
137-
|| remaining.is_escaped(i) != state.node().prefix.is_escaped(i)
151+
|| remaining.is_escaped(i) != state.node_mut().prefix.is_escaped(i)
138152
})
139153
.unwrap_or(len);
140154

141155
// If this node has a longer prefix than we need, we have to fork and extract the
142156
// common prefix into a shared parent.
143-
if state.node().prefix.len() > common_prefix {
144-
let node = state.node();
157+
if state.node_mut().prefix.len() > common_prefix {
158+
let node = state.node_mut();
145159

146160
// Move the non-matching suffix into a child node.
147161
let suffix = node.prefix.as_ref().slice_off(common_prefix).to_owned();
@@ -165,7 +179,7 @@ impl<T> Node<T> {
165179
}
166180

167181
if remaining.len() == common_prefix {
168-
let node = state.node();
182+
let node = state.node_mut();
169183

170184
// This node must not already contain a value.
171185
if node.value.is_some() {
@@ -178,50 +192,91 @@ impl<T> Node<T> {
178192
return Ok(());
179193
}
180194

195+
let common_remaining = remaining;
196+
181197
// Otherwise, the route has a remaining non-matching suffix.
182198
//
183199
// We have to search deeper.
184200
remaining = remaining.slice_off(common_prefix);
185201
let next = remaining[0];
186202

187-
// For parameters with a suffix, we have to find the matching suffix or
188-
// create a new child node.
189-
if matches!(state.node().node_type, NodeType::Param { .. }) {
203+
{
190204
let terminator = remaining
191205
.iter()
192206
.position(|&b| b == b'/')
193207
// Include the '/' in the suffix.
194208
.map(|b| b + 1)
195209
.unwrap_or(remaining.len());
196210

197-
let suffix = remaining.slice_until(terminator);
211+
if let Ok(Some(wildcard)) = find_wildcard(remaining.slice_until(terminator)) {
212+
let suffix = remaining.slice_off(wildcard.end);
198213

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 {
214+
if !matches!(*suffix, b"" | b"/") {
215+
// If we are inserting a suffix and there is a static prefix that already leads to this
216+
// route parameter, we have a prefix-suffix conflict.
217+
for child in &state.node().children {
218+
if state.node().wild_child_in_segment() {
208219
return Err(InsertError::conflict(
209220
&route,
210-
UnescapedRoute::default().as_ref(), // TODO
211-
child,
221+
common_remaining,
222+
state.node(),
212223
));
213224
}
225+
226+
if matches!(child.node_type, NodeType::Static)
227+
&& child.wild_child_in_segment()
228+
{
229+
return Err(InsertError::conflict(&route, remaining, child));
230+
}
214231
}
215232
}
216233
}
234+
}
235+
236+
// For parameters with a suffix, we have to find the matching suffix or create a new child node.
237+
if matches!(state.node_mut().node_type, NodeType::Param { .. }) {
238+
let terminator = remaining
239+
.iter()
240+
.position(|&b| b == b'/')
241+
// Include the '/' in the suffix.
242+
.map(|b| b + 1)
243+
.unwrap_or(remaining.len());
244+
245+
let suffix = remaining.slice_until(terminator);
217246

218-
for (i, child) in state.node().children.iter().enumerate() {
247+
let mut extra_trailing_slash = false;
248+
for (i, child) in state.node_mut().children.iter().enumerate() {
219249
// Find a matching suffix.
220250
if *child.prefix == **suffix {
221-
state.node().priority += 1;
251+
state.node_mut().priority += 1;
222252
state = state.set_child(i);
223253
continue 'walk;
224254
}
255+
256+
// Matches except for an extra trailing slash.
257+
if let Some((common, remaining)) = suffix.split_at_checked(child.prefix.len()) {
258+
if *common == *child.prefix && remaining == *b"/" {
259+
extra_trailing_slash = true;
260+
}
261+
}
262+
}
263+
264+
// If we are inserting a conflicting suffix, and there is a static prefix that already leads to
265+
// this route parameter, we have a prefix-suffix conflict.
266+
if !extra_trailing_slash && !matches!(*suffix, b"" | b"/") {
267+
if let Some(parent) = state.parent_mut() {
268+
if parent.wild_child_in_segment() {
269+
return Err(InsertError::conflict(&route, common_remaining, parent));
270+
}
271+
272+
for child in &parent.children {
273+
if matches!(child.node_type, NodeType::Static)
274+
&& child.wild_child_in_segment()
275+
{
276+
return Err(InsertError::conflict(&route, common_remaining, child));
277+
}
278+
}
279+
}
225280
}
226281

227282
// Multiple parameters within the same segment, e.g. `/{foo}{bar}`.
@@ -230,19 +285,20 @@ impl<T> Node<T> {
230285
}
231286

232287
// If there is no matching suffix, create a new suffix node.
233-
let child = state.node().add_suffix_child(Node {
288+
let child = state.node_mut().add_suffix_child(Node {
234289
prefix: suffix.to_owned(),
235290
node_type: NodeType::Static,
236291
priority: 1,
237292
..Node::default()
238293
});
239-
state.node().node_type = NodeType::Param { suffix: true };
294+
let has_suffix = !matches!(*suffix, b"" | b"/");
295+
state.node_mut().node_type = NodeType::Param { suffix: has_suffix };
240296
state = state.set_child(child);
241297

242298
// If this is the final route segment, insert the value.
243299
if terminator == remaining.len() {
244-
state.node().value = Some(UnsafeCell::new(val));
245-
state.node().remapping = remapping;
300+
state.node_mut().value = Some(UnsafeCell::new(val));
301+
state.node_mut().remapping = remapping;
246302
return Ok(());
247303
}
248304

@@ -252,31 +308,31 @@ impl<T> Node<T> {
252308

253309
// Create a static node unless we are inserting a parameter.
254310
if remaining[0] != b'{' || remaining.is_escaped(0) {
255-
let child = state.node().add_child(Node {
311+
let child = state.node_mut().add_child(Node {
256312
node_type: NodeType::Static,
257313
priority: 1,
258314
..Node::default()
259315
});
260-
state.node().indices.push(remaining[0]);
316+
state.node_mut().indices.push(remaining[0]);
261317
state = state.set_child(child);
262318
}
263319

264320
// Insert the remaining route.
265-
let last = state.node().insert_route(remaining, val)?;
321+
let last = state.node_mut().insert_route(remaining, val)?;
266322
last.remapping = remapping;
267323
return Ok(());
268324
}
269325

270326
// Find a child node that matches the next character in the route.
271-
for mut i in 0..state.node().indices.len() {
272-
if next == state.node().indices[i] {
327+
for mut i in 0..state.node_mut().indices.len() {
328+
if next == state.node_mut().indices[i] {
273329
// Make sure not confuse the start of a wildcard with an escaped `{` or `}`.
274330
if matches!(next, b'{' | b'}') && !remaining.is_escaped(0) {
275331
continue;
276332
}
277333

278334
// Continue searching in the child.
279-
i = state.node().update_child_priority(i);
335+
i = state.node_mut().update_child_priority(i);
280336
state = state.set_child(i);
281337
continue 'walk;
282338
}
@@ -286,17 +342,17 @@ impl<T> Node<T> {
286342
//
287343
// If we're not inserting a wildcard we have to create a static child.
288344
if (next != b'{' || remaining.is_escaped(0))
289-
&& state.node().node_type != NodeType::CatchAll
345+
&& state.node_mut().node_type != NodeType::CatchAll
290346
{
291-
let node = state.node();
347+
let node = state.node_mut();
292348

293349
let terminator = remaining
294350
.iter()
295351
.position(|&b| b == b'/')
296352
.unwrap_or(remaining.len());
297353

298354
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
355+
// If we are inserting a parameter prefix and this node already has a parameter suffix, we
300356
// have a prefix-suffix conflict.
301357
if wildcard.start > 0 && node.wild_child {
302358
let wild_child = node.children.last().unwrap();
@@ -320,35 +376,105 @@ impl<T> Node<T> {
320376
// We're trying to insert a wildcard.
321377
//
322378
// If this node already has a wildcard child, we have to make sure it matches.
323-
if state.node().wild_child {
379+
if state.node_mut().wild_child {
324380
// Wildcards are always the last child.
325-
let wild_child = state.node().children.len() - 1;
381+
let wild_child = state.node_mut().children.len() - 1;
326382
state = state.set_child(wild_child);
327-
state.node().priority += 1;
383+
state.node_mut().priority += 1;
328384

329385
// Make sure the route parameter matches.
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()));
386+
if let Some(wildcard) = remaining.get(..state.node_mut().prefix.len()) {
387+
if *wildcard != *state.node_mut().prefix {
388+
return Err(InsertError::conflict(&route, remaining, state.node_mut()));
333389
}
334390
}
335391

336392
// Catch-all routes cannot have children.
337-
if state.node().node_type == NodeType::CatchAll {
338-
return Err(InsertError::conflict(&route, remaining, state.node()));
393+
if state.node_mut().node_type == NodeType::CatchAll {
394+
return Err(InsertError::conflict(&route, remaining, state.node_mut()));
395+
}
396+
397+
if let Some(parent) = state.parent() {
398+
// If there is a route with both a prefix and a suffix, and we are inserting a route with a
399+
// matching prefix but _without_ a suffix, we have a prefix-suffix conflict.
400+
if !parent.prefix.ends_with(b"/")
401+
&& !state.node().prefix.starts_with(b"/")
402+
&& matches!(state.node().node_type, NodeType::Param { suffix: true })
403+
{
404+
let terminator = remaining
405+
.iter()
406+
.position(|&b| b == b'/')
407+
// Include the '/' in the suffix.
408+
.map(|b| b + 1)
409+
.unwrap_or(remaining.len());
410+
411+
if let Ok(Some(wildcard)) = find_wildcard(remaining.slice_until(terminator))
412+
{
413+
let suffix = remaining.slice_off(wildcard.end);
414+
415+
if matches!(*suffix, b"" | b"/") {
416+
return Err(InsertError::conflict(&route, remaining, parent));
417+
}
418+
}
419+
}
339420
}
340421

341422
// Continue with the wildcard node.
342423
continue 'walk;
343424
}
344425

426+
if let Ok(Some(wildcard)) = find_wildcard(remaining) {
427+
let node = state.node_mut();
428+
let suffix = remaining.slice_off(wildcard.end);
429+
430+
// If we are inserting a suffix and there is a static prefix that already leads to this
431+
// route parameter, we have a prefix-suffix conflict.
432+
if !matches!(*suffix, b"" | b"/") {
433+
for child in &node.children {
434+
if node.wild_child_in_segment() {
435+
return Err(InsertError::conflict(&route, common_remaining, node));
436+
}
437+
438+
if matches!(child.node_type, NodeType::Static)
439+
&& child.wild_child_in_segment()
440+
{
441+
return Err(InsertError::conflict(&route, remaining, child));
442+
}
443+
}
444+
}
445+
}
446+
345447
// Otherwise, create a new node for the wildcard and insert the route.
346-
let last = state.node().insert_route(remaining, val)?;
448+
let last = state.node_mut().insert_route(remaining, val)?;
347449
last.remapping = remapping;
348450
return Ok(());
349451
}
350452
}
351453

454+
/// Returns `true` if there is a wildcard parameter node within the current route segment,
455+
/// i.e. before a trailing slash.
456+
fn wild_child_in_segment(&self) -> bool {
457+
if self.prefix.last() == Some(&b'/') {
458+
return false;
459+
}
460+
461+
if self.wild_child {
462+
return true;
463+
}
464+
465+
for child in &self.children {
466+
if child.prefix.contains(&b'/') {
467+
return false;
468+
}
469+
470+
if child.wild_child || child.wild_child_in_segment() {
471+
return true;
472+
}
473+
}
474+
475+
false
476+
}
477+
352478
// Insert a route at this node.
353479
//
354480
// If the route starts with a wildcard, a child node will be created for the parameter

0 commit comments

Comments
 (0)