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