1use anyhow::Result;
2use either::Either;
3use turbo_rcstr::RcStr;
4use turbo_tasks::{
5 FxIndexSet, ResolvedVc, ValueToString, Vc,
6 graph::{AdjacencyMap, GraphTraversal},
7};
8use turbo_tasks_fs::FileSystemPath;
9
10use crate::asset::Asset;
11
12#[turbo_tasks::value(transparent)]
13pub struct OptionOutputAsset(Option<ResolvedVc<Box<dyn OutputAsset>>>);
14
15#[turbo_tasks::value_trait]
16pub trait OutputAssetsReference {
17 #[turbo_tasks::function]
19 fn references(self: Vc<Self>) -> Vc<OutputAssetsWithReferenced> {
20 OutputAssetsWithReferenced {
21 assets: OutputAssets::empty_resolved(),
22 referenced_assets: OutputAssets::empty_resolved(),
23 references: OutputAssetsReferences::empty_resolved(),
24 }
25 .cell()
26 }
27}
28
29#[turbo_tasks::value_trait]
34pub trait OutputAsset: Asset + OutputAssetsReference {
35 #[turbo_tasks::function]
38 fn path(&self) -> Vc<FileSystemPath>;
39
40 #[turbo_tasks::function]
43 fn path_string(self: Vc<Self>) -> Vc<RcStr> {
44 self.path().to_string()
45 }
46}
47
48#[turbo_tasks::value(transparent)]
49pub struct OutputAssetsReferences(Vec<ResolvedVc<Box<dyn OutputAssetsReference>>>);
50
51#[turbo_tasks::value_impl]
52impl OutputAssetsReferences {
53 #[turbo_tasks::function]
54 pub async fn concatenate(&self, other: Vc<Self>) -> Result<Vc<Self>> {
55 let mut references: FxIndexSet<_> = self.0.iter().copied().collect();
56 references.extend(other.await?.iter().copied());
57 Ok(Vc::cell(references.into_iter().collect()))
58 }
59}
60impl OutputAssetsReferences {
61 pub fn empty() -> Vc<Self> {
62 Vc::cell(vec![])
63 }
64
65 pub fn empty_resolved() -> ResolvedVc<Self> {
66 ResolvedVc::cell(vec![])
67 }
68}
69
70#[turbo_tasks::value(transparent)]
71pub struct OutputAssets(Vec<ResolvedVc<Box<dyn OutputAsset>>>);
72
73#[turbo_tasks::value_impl]
74impl OutputAssets {
75 #[turbo_tasks::function]
76 pub async fn concatenate(&self, other: Vc<Self>) -> Result<Vc<Self>> {
77 let mut assets: FxIndexSet<_> = self.0.iter().copied().collect();
78 assets.extend(other.await?.iter().copied());
79 Ok(Vc::cell(assets.into_iter().collect()))
80 }
81
82 #[turbo_tasks::function]
83 pub async fn concat_asset(&self, asset: ResolvedVc<Box<dyn OutputAsset>>) -> Result<Vc<Self>> {
84 let mut assets: FxIndexSet<_> = self.0.iter().copied().collect();
85 assets.extend([asset]);
86 Ok(Vc::cell(assets.into_iter().collect()))
87 }
88
89 #[turbo_tasks::function]
90 pub async fn concat(other: Vec<Vc<Self>>) -> Result<Vc<Self>> {
91 let mut assets: FxIndexSet<_> = FxIndexSet::default();
92 for other in other {
93 assets.extend(other.await?.iter().copied());
94 }
95 Ok(Vc::cell(assets.into_iter().collect()))
96 }
97}
98
99impl OutputAssets {
100 pub fn empty() -> Vc<Self> {
101 Vc::cell(vec![])
102 }
103
104 pub fn empty_resolved() -> ResolvedVc<Self> {
105 ResolvedVc::cell(vec![])
106 }
107}
108
109#[turbo_tasks::value(transparent)]
110pub struct ExpandedOutputAssets(Vec<ResolvedVc<Box<dyn OutputAsset>>>);
111
112#[turbo_tasks::value(transparent)]
114pub struct OutputAssetsSet(
115 #[bincode(with = "turbo_bincode::indexset")] FxIndexSet<ResolvedVc<Box<dyn OutputAsset>>>,
116);
117
118#[turbo_tasks::value(shared)]
119#[derive(Clone)]
120pub struct OutputAssetsWithReferenced {
121 pub assets: ResolvedVc<OutputAssets>,
123 pub referenced_assets: ResolvedVc<OutputAssets>,
125 pub references: ResolvedVc<OutputAssetsReferences>,
129}
130
131impl OutputAssetsWithReferenced {
132 async fn expand_assets(
133 &self,
134 inner_output_assets: bool,
135 ) -> Result<Vec<ResolvedVc<Box<dyn OutputAsset>>>> {
136 expand_output_assets(
137 self.assets
138 .await?
139 .into_iter()
140 .chain(self.referenced_assets.await?)
141 .map(ExpandOutputAssetsInput::Asset)
142 .chain(
143 self.references
144 .await?
145 .into_iter()
146 .map(ExpandOutputAssetsInput::Reference),
147 ),
148 inner_output_assets,
149 )
150 .await
151 }
152}
153
154#[turbo_tasks::value_impl]
155impl OutputAssetsWithReferenced {
156 #[turbo_tasks::function]
157 pub fn from_assets(assets: ResolvedVc<OutputAssets>) -> Vc<Self> {
158 OutputAssetsWithReferenced {
159 assets,
160 referenced_assets: OutputAssets::empty_resolved(),
161 references: OutputAssetsReferences::empty_resolved(),
162 }
163 .cell()
164 }
165
166 #[turbo_tasks::function]
167 pub async fn concatenate(&self, other: Vc<Self>) -> Result<Vc<Self>> {
168 let other = other.await?;
169 Ok(Self {
170 assets: self.assets.concatenate(*other.assets).to_resolved().await?,
171 referenced_assets: self
172 .referenced_assets
173 .concatenate(*other.referenced_assets)
174 .to_resolved()
175 .await?,
176 references: self
177 .references
178 .concatenate(*other.references)
179 .to_resolved()
180 .await?,
181 }
182 .cell())
183 }
184
185 #[turbo_tasks::function]
186 pub async fn concatenate_asset(
187 &self,
188 asset: ResolvedVc<Box<dyn OutputAsset>>,
189 ) -> Result<Vc<Self>> {
190 Ok(Self {
191 assets: self.assets.concat_asset(*asset).to_resolved().await?,
192 referenced_assets: self.referenced_assets,
193 references: self.references,
194 }
195 .cell())
196 }
197
198 #[turbo_tasks::function]
200 pub async fn expand_all_assets(&self) -> Result<Vc<ExpandedOutputAssets>> {
201 Ok(Vc::cell(self.expand_assets(true).await?))
202 }
203
204 #[turbo_tasks::function]
207 pub async fn all_assets(&self) -> Result<Vc<OutputAssets>> {
208 Ok(Vc::cell(self.expand_assets(false).await?))
209 }
210
211 #[turbo_tasks::function]
214 pub fn primary_assets(&self) -> Vc<OutputAssets> {
215 *self.assets
216 }
217
218 #[turbo_tasks::function]
221 pub async fn referenced_assets(&self) -> Result<Vc<OutputAssets>> {
222 Ok(Vc::cell(
223 expand_output_assets(
224 self.referenced_assets
225 .await?
226 .into_iter()
227 .map(ExpandOutputAssetsInput::Asset)
228 .chain(
229 self.references
230 .await?
231 .into_iter()
232 .map(ExpandOutputAssetsInput::Reference),
233 ),
234 false,
235 )
236 .await?,
237 ))
238 }
239}
240
241async fn get_referenced_assets(
243 inner_output_assets: bool,
244 input: ExpandOutputAssetsInput,
245) -> Result<impl Iterator<Item = ExpandOutputAssetsInput>> {
246 let refs = match input {
247 ExpandOutputAssetsInput::Asset(output_asset) => {
248 if !inner_output_assets {
249 return Ok(Either::Left(std::iter::empty()));
250 }
251 output_asset.references().await?
252 }
253 ExpandOutputAssetsInput::Reference(reference) => reference.references().await?,
254 };
255 let assets = refs
256 .assets
257 .await?
258 .into_iter()
259 .chain(refs.referenced_assets.await?)
260 .map(ExpandOutputAssetsInput::Asset)
261 .chain(
262 refs.references
263 .await?
264 .into_iter()
265 .map(ExpandOutputAssetsInput::Reference),
266 );
267 Ok(Either::Right(assets))
268}
269
270#[derive(PartialEq, Eq, Hash, Clone, Copy)]
271pub enum ExpandOutputAssetsInput {
272 Asset(ResolvedVc<Box<dyn OutputAsset>>),
273 Reference(ResolvedVc<Box<dyn OutputAssetsReference>>),
274}
275
276pub async fn expand_output_assets(
277 inputs: impl Iterator<Item = ExpandOutputAssetsInput>,
278 inner_output_assets: bool,
279) -> Result<Vec<ResolvedVc<Box<dyn OutputAsset>>>> {
280 let edges = AdjacencyMap::new()
281 .visit(inputs, async |input| {
282 get_referenced_assets(inner_output_assets, input).await
283 })
284 .await
285 .completed()?
286 .into_postorder_topological();
287
288 let mut assets = Vec::new();
289 for input in edges {
290 match input {
291 ExpandOutputAssetsInput::Asset(asset) => {
292 assets.push(asset);
293 }
294 ExpandOutputAssetsInput::Reference(_) => {}
295 }
296 }
297
298 Ok(assets)
299}