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