1use smallvec::SmallVec;
2
3#[derive(Debug, PartialEq)]
6pub struct Span {
7 start: usize,
8 end: usize,
9}
10
11pub fn split(path: &'static str) -> Vec<Span> {
12 let mut segments = Vec::new();
13 let mut start = None;
14
15 for (index, byte) in path.bytes().enumerate() {
16 match (start, byte) {
17 (Some(from), b'/') => {
20 segments.push(Span::new(from, index));
22
23 start = None;
25 }
26
27 (Some(_), _) => {}
30
31 (None, b'/') => {}
34
35 (None, _) => {
38 start = Some(index);
40 }
41 }
42 }
43
44 if let Some(from) = start {
48 segments.push(Span::new(from, path.len()));
49 }
50
51 segments
52}
53
54#[inline]
55pub fn split_into(segments: &mut SmallVec<[Span; 5]>, path: &str) {
56 let mut start = None;
57
58 for (index, byte) in path.chars().enumerate() {
59 match (start, byte) {
60 (Some(from), '/') => {
63 segments.push(Span::new(from, index));
65
66 start = None;
68 }
69
70 (Some(_), _) => {}
73
74 (None, '/') => {}
77
78 (None, _) => {
81 start = Some(index);
83 }
84 }
85 }
86
87 if let Some(from) = start {
91 segments.push(Span::new(from, path.len()));
92 }
93}
94
95impl Span {
96 pub fn start(&self) -> usize {
99 self.start
100 }
101
102 pub fn end(&self) -> usize {
105 self.end
106 }
107}
108
109impl Span {
110 pub(crate) fn new(start: usize, end: usize) -> Self {
111 Self { start, end }
112 }
113}
114
115impl Clone for Span {
116 fn clone(&self) -> Self {
117 Self {
118 start: self.start,
119 end: self.end,
120 }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use smallvec::SmallVec;
127
128 use super::{split, split_into, Span};
129
130 const PATHS: [&str; 15] = [
131 "/home/about",
132 "/products/item/123",
133 "/blog/posts/2024/june",
134 "/user/profile/settings",
135 "/services/contact",
136 "/search/results?q=books",
137 "/news/latest",
138 "/portfolio/designs",
139 "/faq",
140 "/support/tickets",
141 "//home//about",
142 "/products//item/123",
143 "/blog/posts/2024//june",
144 "/user//profile/settings",
145 "/services/contact//us",
146 ];
147
148 fn get_expected_results() -> [Vec<Span>; 15] {
149 [
150 vec![Span::new(1, 5), Span::new(6, 11)],
151 vec![Span::new(1, 9), Span::new(10, 14), Span::new(15, 18)],
152 vec![
153 Span::new(1, 5),
154 Span::new(6, 11),
155 Span::new(12, 16),
156 Span::new(17, 21),
157 ],
158 vec![Span::new(1, 5), Span::new(6, 13), Span::new(14, 22)],
159 vec![Span::new(1, 9), Span::new(10, 17)],
160 vec![Span::new(1, 7), Span::new(8, 23)],
161 vec![Span::new(1, 5), Span::new(6, 12)],
162 vec![Span::new(1, 10), Span::new(11, 18)],
163 vec![Span::new(1, 4)],
164 vec![Span::new(1, 8), Span::new(9, 16)],
165 vec![Span::new(2, 6), Span::new(8, 13)],
166 vec![Span::new(1, 9), Span::new(11, 15), Span::new(16, 19)],
167 vec![
168 Span::new(1, 5),
169 Span::new(6, 11),
170 Span::new(12, 16),
171 Span::new(18, 22),
172 ],
173 vec![Span::new(1, 5), Span::new(7, 14), Span::new(15, 23)],
174 vec![Span::new(1, 9), Span::new(10, 17), Span::new(19, 21)],
175 ]
176 }
177
178 #[test]
179 fn test_split() {
180 let expected_results = get_expected_results();
181
182 for (path_index, path_value) in PATHS.iter().enumerate() {
183 let segments = split(path_value);
184
185 for (segment_index, segment_value) in segments.into_iter().enumerate() {
186 assert_eq!(segment_value, expected_results[path_index][segment_index]);
187 }
188 }
189 }
190
191 #[test]
192 fn test_split_into() {
193 let expected_results = get_expected_results();
194
195 for (path_index, path_value) in PATHS.iter().enumerate() {
196 let mut segments = SmallVec::new();
197
198 split_into(&mut segments, path_value);
199
200 for (segment_index, segment_value) in segments.into_iter().enumerate() {
201 assert_eq!(segment_value, expected_results[path_index][segment_index]);
202 }
203 }
204 }
205}