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