turbopack_dev_server/source/
combined.rs

1use anyhow::Result;
2use turbo_rcstr::{RcStr, rcstr};
3use turbo_tasks::{ResolvedVc, TryJoinIterExt, Vc};
4use turbopack_core::introspect::{Introspectable, IntrospectableChildren};
5
6use crate::source::{
7    ContentSource, ContentSources,
8    route_tree::{RouteTree, RouteTrees},
9};
10
11/// Combines multiple [ContentSource]s by trying all content sources in order.
12///
13/// The content source which responds with the most specific response (that is
14/// not a [ContentSourceContent::NotFound]) will be returned.
15#[turbo_tasks::value(shared)]
16pub struct CombinedContentSource {
17    pub sources: Vec<ResolvedVc<Box<dyn ContentSource>>>,
18}
19
20impl CombinedContentSource {
21    pub fn new(sources: Vec<ResolvedVc<Box<dyn ContentSource>>>) -> Vc<Self> {
22        CombinedContentSource { sources }.cell()
23    }
24}
25
26#[turbo_tasks::value_impl]
27impl ContentSource for CombinedContentSource {
28    #[turbo_tasks::function]
29    async fn get_routes(&self) -> Result<Vc<RouteTree>> {
30        let all_routes = self
31            .sources
32            .iter()
33            .map(|s| async move { s.get_routes().to_resolved().await })
34            .try_join()
35            .await?;
36        Ok(Vc::<RouteTrees>::cell(all_routes).merge())
37    }
38
39    #[turbo_tasks::function]
40    fn get_children(&self) -> Vc<ContentSources> {
41        Vc::cell(self.sources.clone())
42    }
43}
44
45#[turbo_tasks::value_impl]
46impl Introspectable for CombinedContentSource {
47    #[turbo_tasks::function]
48    fn ty(&self) -> Vc<RcStr> {
49        Vc::cell(rcstr!("combined content source"))
50    }
51
52    #[turbo_tasks::function]
53    async fn title(&self) -> Result<Vc<RcStr>> {
54        let titles = self
55            .sources
56            .iter()
57            .map(|&source| async move {
58                Ok(
59                    if let Some(source) =
60                        ResolvedVc::try_sidecast::<Box<dyn Introspectable>>(source)
61                    {
62                        Some(source.title().await?)
63                    } else {
64                        None
65                    },
66                )
67            })
68            .try_join()
69            .await?;
70        let mut titles = titles.into_iter().flatten().collect::<Vec<_>>();
71        titles.sort();
72        const NUMBER_OF_TITLES_TO_DISPLAY: usize = 5;
73        let mut titles = titles
74            .iter()
75            .map(|t| t.as_str())
76            .filter(|t| !t.is_empty())
77            .take(NUMBER_OF_TITLES_TO_DISPLAY + 1)
78            .collect::<Vec<_>>();
79        if titles.len() > NUMBER_OF_TITLES_TO_DISPLAY {
80            titles[NUMBER_OF_TITLES_TO_DISPLAY] = "...";
81        }
82        Ok(Vc::cell(titles.join(", ").into()))
83    }
84
85    #[turbo_tasks::function]
86    async fn children(&self) -> Result<Vc<IntrospectableChildren>> {
87        Ok(Vc::cell(
88            self.sources
89                .iter()
90                .copied()
91                .map(|s| async move { Ok(ResolvedVc::try_sidecast::<Box<dyn Introspectable>>(s)) })
92                .try_join()
93                .await?
94                .into_iter()
95                .flatten()
96                .map(|i| (rcstr!("source"), i))
97                .collect(),
98        ))
99    }
100}