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(other: Vec<Vc<Self>>) -> Result<Vc<Self>> {
87 let mut assets: FxIndexSet<_> = FxIndexSet::default();
88 for other in other {
89 assets.extend(other.await?.iter().copied());
90 }
91 Ok(Vc::cell(assets.into_iter().collect()))
92 }
93}
94
95impl OutputAssets {
96 pub fn empty() -> Vc<Self> {
97 Vc::cell(vec![])
98 }
99
100 pub fn empty_resolved() -> ResolvedVc<Self> {
101 ResolvedVc::cell(vec![])
102 }
103}
104
105#[turbo_tasks::value(transparent)]
106pub struct ExpandedOutputAssets(Vec<ResolvedVc<Box<dyn OutputAsset>>>);
107
108#[turbo_tasks::value(transparent)]
110pub struct OutputAssetsSet(
111 #[bincode(with = "turbo_bincode::indexset")] FxIndexSet<ResolvedVc<Box<dyn OutputAsset>>>,
112);
113
114#[turbo_tasks::value(shared)]
115#[derive(Clone)]
116pub struct OutputAssetsWithReferenced {
117 pub assets: ResolvedVc<OutputAssets>,
119 pub referenced_assets: ResolvedVc<OutputAssets>,
121 pub references: ResolvedVc<OutputAssetsReferences>,
125}
126
127impl OutputAssetsWithReferenced {
128 async fn expand_assets(
129 &self,
130 inner_output_assets: bool,
131 ) -> Result<Vec<ResolvedVc<Box<dyn OutputAsset>>>> {
132 expand_output_assets(
133 self.assets
134 .await?
135 .into_iter()
136 .chain(self.referenced_assets.await?.into_iter())
137 .map(|&asset| ExpandOutputAssetsInput::Asset(asset))
138 .chain(
139 self.references
140 .await?
141 .into_iter()
142 .map(|&reference| ExpandOutputAssetsInput::Reference(reference)),
143 ),
144 inner_output_assets,
145 )
146 .await
147 }
148}
149
150#[turbo_tasks::value_impl]
151impl OutputAssetsWithReferenced {
152 #[turbo_tasks::function]
153 pub fn from_assets(assets: ResolvedVc<OutputAssets>) -> Vc<Self> {
154 OutputAssetsWithReferenced {
155 assets,
156 referenced_assets: OutputAssets::empty_resolved(),
157 references: OutputAssetsReferences::empty_resolved(),
158 }
159 .cell()
160 }
161
162 #[turbo_tasks::function]
163 pub async fn concatenate(&self, other: Vc<Self>) -> Result<Vc<Self>> {
164 Ok(Self {
165 assets: self
166 .assets
167 .concatenate(*other.await?.assets)
168 .to_resolved()
169 .await?,
170 referenced_assets: self
171 .referenced_assets
172 .concatenate(*other.await?.referenced_assets)
173 .to_resolved()
174 .await?,
175 references: self
176 .references
177 .concatenate(*other.await?.references)
178 .to_resolved()
179 .await?,
180 }
181 .cell())
182 }
183
184 #[turbo_tasks::function]
186 pub async fn expand_all_assets(&self) -> Result<Vc<ExpandedOutputAssets>> {
187 Ok(Vc::cell(self.expand_assets(true).await?))
188 }
189
190 #[turbo_tasks::function]
193 pub async fn all_assets(&self) -> Result<Vc<OutputAssets>> {
194 Ok(Vc::cell(self.expand_assets(false).await?))
195 }
196
197 #[turbo_tasks::function]
200 pub fn primary_assets(&self) -> Vc<OutputAssets> {
201 *self.assets
202 }
203
204 #[turbo_tasks::function]
207 pub async fn referenced_assets(&self) -> Result<Vc<OutputAssets>> {
208 Ok(Vc::cell(
209 expand_output_assets(
210 self.referenced_assets
211 .await?
212 .into_iter()
213 .copied()
214 .map(ExpandOutputAssetsInput::Asset)
215 .chain(
216 self.references
217 .await?
218 .into_iter()
219 .copied()
220 .map(ExpandOutputAssetsInput::Reference),
221 ),
222 false,
223 )
224 .await?,
225 ))
226 }
227}
228
229async fn get_referenced_assets(
231 inner_output_assets: bool,
232 input: ExpandOutputAssetsInput,
233) -> Result<impl Iterator<Item = ExpandOutputAssetsInput>> {
234 let refs = match input {
235 ExpandOutputAssetsInput::Asset(output_asset) => {
236 if !inner_output_assets {
237 return Ok(Either::Left(std::iter::empty()));
238 }
239 output_asset.references().await?
240 }
241 ExpandOutputAssetsInput::Reference(reference) => reference.references().await?,
242 };
243 let assets = refs
244 .assets
245 .await?
246 .into_iter()
247 .chain(refs.referenced_assets.await?.into_iter())
248 .map(|&asset| ExpandOutputAssetsInput::Asset(asset))
249 .chain(
250 refs.references
251 .await?
252 .into_iter()
253 .map(|&reference| ExpandOutputAssetsInput::Reference(reference)),
254 );
255 Ok(Either::Right(assets))
256}
257
258#[derive(PartialEq, Eq, Hash, Clone, Copy)]
259pub enum ExpandOutputAssetsInput {
260 Asset(ResolvedVc<Box<dyn OutputAsset>>),
261 Reference(ResolvedVc<Box<dyn OutputAssetsReference>>),
262}
263
264pub async fn expand_output_assets(
265 inputs: impl Iterator<Item = ExpandOutputAssetsInput>,
266 inner_output_assets: bool,
267) -> Result<Vec<ResolvedVc<Box<dyn OutputAsset>>>> {
268 let edges = AdjacencyMap::new()
269 .visit(inputs, async |input| {
270 get_referenced_assets(inner_output_assets, input).await
271 })
272 .await
273 .completed()?
274 .into_postorder_topological();
275
276 let mut assets = Vec::new();
277 for input in edges {
278 match input {
279 ExpandOutputAssetsInput::Asset(asset) => {
280 assets.push(asset);
281 }
282 ExpandOutputAssetsInput::Reference(_) => {}
283 }
284 }
285
286 Ok(assets)
287}