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