turbopack_dev_server/source/
route_tree.rs1use std::{fmt::Write, mem::replace};
2
3use anyhow::Result;
4use bincode::{Decode, Encode};
5use turbo_rcstr::RcStr;
6use turbo_tasks::{
7 FxIndexMap, NonLocalValue, ReadRef, ResolvedVc, TaskInput, TryJoinIterExt, ValueToString, Vc,
8 fxindexmap, trace::TraceRawVcs,
9};
10
11use crate::source::{GetContentSourceContent, GetContentSourceContents};
12
13#[derive(
16 TaskInput, Clone, Debug, PartialEq, Eq, Hash, TraceRawVcs, NonLocalValue, Encode, Decode,
17)]
18pub enum RouteType {
19 Exact,
20 CatchAll,
21 Fallback,
22 NotFound,
23}
24
25#[derive(
27 TaskInput, Clone, Debug, PartialEq, Eq, Hash, TraceRawVcs, NonLocalValue, Encode, Decode,
28)]
29pub enum BaseSegment {
30 Static(RcStr),
31 Dynamic,
32}
33
34impl BaseSegment {
35 pub fn from_static_pathname(str: &str) -> impl Iterator<Item = BaseSegment> + '_ {
36 str.split('/')
37 .filter(|s| !s.is_empty())
38 .map(|s| BaseSegment::Static(s.into()))
39 }
40}
41
42#[turbo_tasks::value(transparent)]
48pub struct RouteTrees(Vec<ResolvedVc<RouteTree>>);
49
50#[turbo_tasks::value_impl]
51impl RouteTrees {
52 #[turbo_tasks::function]
54 pub async fn merge(self: Vc<Self>) -> Result<Vc<RouteTree>> {
55 let trees = &*self.await?;
56 if trees.is_empty() {
57 return Ok(RouteTree::default().cell());
58 }
59 if trees.len() == 1 {
60 return Ok(**trees.iter().next().unwrap());
61 }
62
63 let mut tree_values = trees.iter().try_join().await?;
65 let mut common_base = 0;
66 let last_tree = tree_values.pop().unwrap();
67 'outer: while common_base < last_tree.base.len() {
68 for tree in tree_values.iter() {
69 if tree.base.len() <= common_base {
70 break 'outer;
71 }
72 if tree.base[common_base] != last_tree.base[common_base] {
73 break 'outer;
74 }
75 }
76 common_base += 1;
77 }
78 tree_values.push(last_tree);
79
80 let trees = trees
82 .iter()
83 .enumerate()
84 .map(|(i, tree)| {
85 if tree_values[i].base.len() > common_base {
86 tree.with_base_len(common_base)
87 } else {
88 **tree
89 }
90 })
91 .collect::<Vec<_>>();
92
93 let tree_values = trees.into_iter().try_join().await?;
95 let mut iter = tree_values.iter().map(|rr| &**rr);
96 let mut merged = iter.next().unwrap().clone();
97 merged.flat_merge(iter).await?;
98
99 Ok(merged.cell())
100 }
101}
102
103#[turbo_tasks::value]
105#[derive(Default, Clone, Debug)]
106pub struct RouteTree {
107 base: Vec<BaseSegment>,
108 sources: Vec<ResolvedVc<Box<dyn GetContentSourceContent>>>,
109 #[bincode(with = "turbo_bincode::indexmap")]
110 static_segments: FxIndexMap<RcStr, ResolvedVc<RouteTree>>,
111 dynamic_segments: Vec<ResolvedVc<RouteTree>>,
112 catch_all_sources: Vec<ResolvedVc<Box<dyn GetContentSourceContent>>>,
113 fallback_sources: Vec<ResolvedVc<Box<dyn GetContentSourceContent>>>,
114 not_found_sources: Vec<ResolvedVc<Box<dyn GetContentSourceContent>>>,
115}
116
117impl RouteTree {
118 pub fn new_route_ref(
120 base_segments: Vec<BaseSegment>,
121 route_type: RouteType,
122 source: ResolvedVc<Box<dyn GetContentSourceContent>>,
123 ) -> Self {
124 match route_type {
125 RouteType::Exact => Self {
126 base: base_segments,
127 sources: vec![source],
128 ..Default::default()
129 },
130 RouteType::CatchAll => Self {
131 base: base_segments,
132 catch_all_sources: vec![source],
133 ..Default::default()
134 },
135 RouteType::Fallback => Self {
136 base: base_segments,
137 fallback_sources: vec![source],
138 ..Default::default()
139 },
140 RouteType::NotFound => Self {
141 base: base_segments,
142 not_found_sources: vec![source],
143 ..Default::default()
144 },
145 }
146 }
147
148 async fn flat_merge(&mut self, others: impl IntoIterator<Item = &Self> + '_) -> Result<()> {
149 let mut static_segments = FxIndexMap::default();
150 for other in others {
151 debug_assert_eq!(self.base, other.base);
152 self.sources.extend(other.sources.iter().copied());
153 self.catch_all_sources
154 .extend(other.catch_all_sources.iter().copied());
155 self.fallback_sources
156 .extend(other.fallback_sources.iter().copied());
157 self.not_found_sources
158 .extend(other.not_found_sources.iter().copied());
159 for (key, value) in other.static_segments.iter() {
160 if let Some((key, self_value)) = self.static_segments.swap_remove_entry(key) {
161 static_segments.insert(key, vec![self_value, *value]);
162 } else if let Some(list) = static_segments.get_mut(key) {
163 list.push(*value);
164 } else {
165 static_segments.insert(key.clone(), vec![*value]);
166 }
167 }
168 self.dynamic_segments
169 .extend(other.dynamic_segments.iter().copied());
170 }
171 self.static_segments.extend(
172 static_segments
173 .into_iter()
174 .map(|(key, value)| async {
175 Ok((
176 key,
177 if value.len() == 1 {
178 value.into_iter().next().unwrap()
179 } else {
180 Vc::<RouteTrees>::cell(value).merge().to_resolved().await?
181 },
182 ))
183 })
184 .try_join()
185 .await?,
186 );
187 Ok(())
188 }
189
190 fn prepend_base(&mut self, segments: Vec<BaseSegment>) {
191 self.base.splice(..0, segments);
192 }
193}
194
195#[turbo_tasks::value_impl]
196impl ValueToString for RouteTree {
197 #[turbo_tasks::function]
198 async fn to_string(&self) -> Result<Vc<RcStr>> {
199 let RouteTree {
200 base,
201 sources,
202 static_segments,
203 dynamic_segments,
204 catch_all_sources,
205 fallback_sources,
206 not_found_sources,
207 } = self;
208 let mut result = "RouteTree(".to_string();
209 for segment in base {
210 match segment {
211 BaseSegment::Static(str) => write!(result, "/{str}")?,
212 BaseSegment::Dynamic => result.push_str("/[dynamic]"),
213 }
214 }
215 if !base.is_empty() {
216 result.push_str(", ");
217 }
218 for (key, tree) in static_segments {
219 let tree = tree.to_string().await?;
220 write!(result, "{key}: {tree}, ")?;
221 }
222 if !sources.is_empty() {
223 write!(result, "{} x source, ", sources.len())?;
224 }
225 if !dynamic_segments.is_empty() {
226 write!(result, "{} x dynamic, ", dynamic_segments.len())?;
227 }
228 if !catch_all_sources.is_empty() {
229 write!(result, "{} x catch-all, ", catch_all_sources.len())?;
230 }
231 if !fallback_sources.is_empty() {
232 write!(result, "{} x fallback, ", fallback_sources.len())?;
233 }
234 if !not_found_sources.is_empty() {
235 write!(result, "{} x not-found, ", not_found_sources.len())?;
236 }
237 if result.ends_with(", ") {
238 result.truncate(result.len() - 2);
239 }
240 result.push(')');
241 Ok(Vc::cell(result.into()))
242 }
243}
244
245#[turbo_tasks::value_impl]
246impl RouteTree {
247 #[turbo_tasks::function]
249 pub fn empty() -> Vc<RouteTree> {
250 RouteTree::default().cell()
251 }
252
253 #[turbo_tasks::function]
255 pub fn new_route(
256 base_segments: Vec<BaseSegment>,
257 route_type: RouteType,
258 source: ResolvedVc<Box<dyn GetContentSourceContent>>,
259 ) -> Vc<Self> {
260 RouteTree::new_route_ref(base_segments, route_type, source).cell()
261 }
262
263 #[turbo_tasks::function]
267 pub async fn get(self: Vc<Self>, path: RcStr) -> Result<Vc<GetContentSourceContents>> {
268 let RouteTree {
269 base,
270 sources,
271 static_segments,
272 dynamic_segments,
273 catch_all_sources,
274 fallback_sources,
275 not_found_sources,
276 } = &*self.await?;
277 let mut results = Vec::new();
278 if path.is_empty() {
279 if !base.is_empty() {
280 return Ok(Vc::cell(vec![]));
281 }
282 results.extend(sources.iter().copied());
283 } else {
284 let mut segments = path.split('/');
285 for base in base.iter() {
286 let Some(segment) = segments.next() else {
287 return Ok(Vc::cell(vec![]));
288 };
289 match base {
290 BaseSegment::Static(str) => {
291 if str != segment {
292 return Ok(Vc::cell(vec![]));
293 }
294 }
295 BaseSegment::Dynamic => {
296 }
298 }
299 }
300
301 if let Some(segment) = segments.next() {
302 let remainder = segments.remainder().unwrap_or("");
303 if let Some(tree) = static_segments.get(segment) {
304 results.extend(tree.get(remainder.into()).await?.iter().copied());
305 }
306 for tree in dynamic_segments.iter() {
307 results.extend(tree.get(remainder.into()).await?.iter().copied());
308 }
309 } else {
310 results.extend(sources.iter().copied());
311 };
312 }
313 results.extend(catch_all_sources.iter().copied());
314 results.extend(fallback_sources.iter().copied());
315 results.extend(not_found_sources.iter().copied());
316 Ok(Vc::cell(results))
317 }
318
319 #[turbo_tasks::function]
321 pub async fn with_prepended_base(
322 self: Vc<Self>,
323 segments: Vec<BaseSegment>,
324 ) -> Result<Vc<RouteTree>> {
325 let mut this = self.owned().await?;
326 this.prepend_base(segments);
327 Ok(this.cell())
328 }
329
330 #[turbo_tasks::function]
331 async fn with_base_len(self: Vc<Self>, base_len: usize) -> Result<Vc<RouteTree>> {
332 let this = self.await?;
333 if this.base.len() > base_len {
334 let mut inner = ReadRef::into_owned(this);
335 let mut drain = inner.base.drain(base_len..);
336 let selector_segment = drain.next().unwrap();
337 let inner_base = drain.collect();
338 let base = replace(&mut inner.base, inner_base);
339 debug_assert!(base.len() == base_len);
340 match selector_segment {
341 BaseSegment::Static(value) => Ok(RouteTree {
342 base,
343 static_segments: fxindexmap! { value => inner.resolved_cell() },
344 ..Default::default()
345 }
346 .cell()),
347 BaseSegment::Dynamic => Ok(RouteTree {
348 base,
349 dynamic_segments: vec![inner.resolved_cell()],
350 ..Default::default()
351 }
352 .cell()),
353 }
354 } else {
355 Ok(self)
356 }
357 }
358
359 #[turbo_tasks::function]
362 pub async fn map_routes(
363 self: Vc<Self>,
364 mapper: Vc<Box<dyn MapGetContentSourceContent>>,
365 ) -> Result<Vc<Self>> {
366 let mut this = self.owned().await?;
367 let RouteTree {
368 base: _,
369 static_segments,
370 dynamic_segments,
371 sources,
372 catch_all_sources,
373 fallback_sources,
374 not_found_sources,
375 } = &mut this;
376
377 for s in sources.iter_mut() {
378 *s = mapper.map_get_content(**s).to_resolved().await?;
379 }
380 for s in catch_all_sources.iter_mut() {
381 *s = mapper.map_get_content(**s).to_resolved().await?;
382 }
383 for s in fallback_sources.iter_mut() {
384 *s = mapper.map_get_content(**s).to_resolved().await?;
385 }
386 for s in not_found_sources.iter_mut() {
387 *s = mapper.map_get_content(**s).to_resolved().await?;
388 }
389 for r in static_segments.values_mut() {
390 *r = r.map_routes(mapper).to_resolved().await?;
391 }
392 for r in dynamic_segments.iter_mut() {
393 *r = r.map_routes(mapper).to_resolved().await?;
394 }
395
396 Ok(this.cell())
397 }
398}
399
400#[turbo_tasks::value_trait]
402pub trait MapGetContentSourceContent {
403 #[turbo_tasks::function]
404 fn map_get_content(
405 self: Vc<Self>,
406 get_content: Vc<Box<dyn GetContentSourceContent>>,
407 ) -> Vc<Box<dyn GetContentSourceContent>>;
408}