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