turbopack_core/
version.rs1use std::sync::Arc;
2
3use anyhow::{Context, Result, bail};
4use turbo_rcstr::RcStr;
5use turbo_tasks::{
6 NonLocalValue, OperationValue, ReadRef, ResolvedVc, State, TraitRef, Vc,
7 debug::ValueDebugFormat, trace::TraceRawVcs,
8};
9use turbo_tasks_hash::HashAlgorithm;
10
11use crate::asset::AssetContent;
12
13#[turbo_tasks::value(transparent)]
14pub struct OptionVersionedContent(Option<ResolvedVc<Box<dyn VersionedContent>>>);
15
16#[turbo_tasks::value_trait]
21pub trait VersionedContent {
22 #[turbo_tasks::function]
26 fn content(self: Vc<Self>) -> Vc<AssetContent>;
27
28 #[turbo_tasks::function]
31 fn version(self: Vc<Self>) -> Vc<Box<dyn Version>>;
32
33 #[turbo_tasks::function]
36 async fn update(self: Vc<Self>, from: Vc<Box<dyn Version>>) -> Result<Vc<Update>> {
37 let to = self.version();
41 let from_ref = from.into_trait_ref().await?;
42 let to_ref = to.into_trait_ref().await?;
43
44 if TraitRef::ptr_eq(&from_ref, &to_ref) {
46 return Ok(Update::None.cell());
47 }
48
49 let from_id = from.id();
54 let to_id = to.id();
55 let from_id = from_id.await?;
56 let to_id = to_id.await?;
57 Ok(if *from_id == *to_id {
58 Update::None.cell()
59 } else {
60 Update::Total(TotalUpdate { to: to_ref }).cell()
61 })
62 }
63}
64
65#[turbo_tasks::value]
67pub struct VersionedAssetContent {
68 asset_content: ReadRef<AssetContent>,
73}
74
75#[turbo_tasks::value_impl]
76impl VersionedContent for VersionedAssetContent {
77 #[turbo_tasks::function]
78 fn content(&self) -> Vc<AssetContent> {
79 (*self.asset_content).clone().cell()
80 }
81
82 #[turbo_tasks::function]
83 async fn version(&self) -> Result<Vc<Box<dyn Version>>> {
84 Ok(Vc::upcast(
85 FileHashVersion::compute(&self.asset_content).await?,
86 ))
87 }
88}
89
90#[turbo_tasks::value_impl]
91impl VersionedAssetContent {
92 #[turbo_tasks::function]
93 pub async fn new(asset_content: Vc<AssetContent>) -> Result<Vc<Self>> {
95 let asset_content = asset_content.await?;
96 Ok(Self::cell(VersionedAssetContent { asset_content }))
97 }
98}
99
100impl From<AssetContent> for Vc<VersionedAssetContent> {
101 fn from(asset_content: AssetContent) -> Self {
102 VersionedAssetContent::new(asset_content.cell())
103 }
104}
105
106impl From<AssetContent> for Vc<Box<dyn VersionedContent>> {
107 fn from(asset_content: AssetContent) -> Self {
108 Vc::upcast(VersionedAssetContent::new(asset_content.cell()))
109 }
110}
111
112pub trait VersionedContentExt: Send {
113 fn versioned(self: Vc<Self>) -> Vc<Box<dyn VersionedContent>>;
114}
115
116impl VersionedContentExt for AssetContent {
117 fn versioned(self: Vc<Self>) -> Vc<Box<dyn VersionedContent>> {
118 Vc::upcast(VersionedAssetContent::new(self))
119 }
120}
121
122#[turbo_tasks::value_trait]
127pub trait Version {
128 #[turbo_tasks::function]
132 fn id(self: Vc<Self>) -> Vc<RcStr>;
133}
134
135#[turbo_tasks::value_trait]
141pub trait MergeableVersionedContent: VersionedContent {
142 #[turbo_tasks::function]
143 fn get_merger(self: Vc<Self>) -> Vc<Box<dyn VersionedContentMerger>>;
144}
145
146#[turbo_tasks::value_trait]
149pub trait VersionedContentMerger {
150 #[turbo_tasks::function]
151 fn merge(self: Vc<Self>, contents: Vc<VersionedContents>) -> Vc<Box<dyn VersionedContent>>;
152}
153
154#[turbo_tasks::value(transparent)]
155pub struct VersionedContents(Vec<ResolvedVc<Box<dyn VersionedContent>>>);
156
157#[turbo_tasks::value(operation)]
158pub struct NotFoundVersion;
159
160#[turbo_tasks::value_impl]
161impl NotFoundVersion {
162 #[turbo_tasks::function]
163 pub fn new() -> Vc<Self> {
164 NotFoundVersion.cell()
165 }
166}
167
168#[turbo_tasks::value_impl]
169impl Version for NotFoundVersion {
170 #[turbo_tasks::function]
171 fn id(&self) -> Vc<RcStr> {
172 Vc::cell(Default::default())
173 }
174}
175
176#[turbo_tasks::value(serialization = "none", shared)]
178#[derive(Debug)]
179pub enum Update {
180 Total(TotalUpdate),
183
184 Partial(PartialUpdate),
187
188 Missing,
190
191 None,
193}
194
195#[derive(PartialEq, Eq, Debug, Clone, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
197pub struct TotalUpdate {
198 #[turbo_tasks(trace_ignore)]
204 pub to: TraitRef<Box<dyn Version>>,
205}
206
207#[derive(PartialEq, Eq, Debug, Clone, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
209pub struct PartialUpdate {
210 #[turbo_tasks(trace_ignore)]
213 pub to: TraitRef<Box<dyn Version>>,
214 #[turbo_tasks(trace_ignore)]
217 pub instruction: Arc<serde_json::Value>,
218}
219
220#[turbo_tasks::value(operation)]
223#[derive(Clone)]
224pub struct FileHashVersion {
225 hash: RcStr,
226}
227
228impl FileHashVersion {
229 pub async fn compute(asset_content: &AssetContent) -> Result<Vc<Self>> {
231 match asset_content {
232 AssetContent::File(file_vc) => {
233 let hash = file_vc
234 .content_hash(HashAlgorithm::Xxh3Hash128Base40)
235 .owned()
236 .await?
237 .context("file not found")?;
238 Ok(Self::cell(FileHashVersion { hash }))
239 }
240 AssetContent::Redirect { .. } => bail!("not a file"),
241 }
242 }
243}
244
245#[turbo_tasks::value_impl]
246impl Version for FileHashVersion {
247 #[turbo_tasks::function]
248 fn id(&self) -> Vc<RcStr> {
249 Vc::cell(self.hash.clone())
250 }
251}
252
253#[derive(Debug, Eq, PartialEq, TraceRawVcs, NonLocalValue, OperationValue)]
256struct VersionRef(
257 #[turbo_tasks(trace_ignore)] TraitRef<Box<dyn Version>>,
261);
262
263#[turbo_tasks::value(serialization = "none")]
264pub struct VersionState {
265 version: State<VersionRef>,
266}
267
268#[turbo_tasks::value_impl]
269impl VersionState {
270 #[turbo_tasks::function]
271 pub fn get(&self) -> Vc<Box<dyn Version>> {
272 TraitRef::cell(self.version.get().0.clone())
273 }
274}
275
276impl VersionState {
277 pub async fn new(version: TraitRef<Box<dyn Version>>) -> Result<Vc<Self>> {
278 Ok(Self::cell(VersionState {
279 version: State::new(VersionRef(version)),
280 }))
281 }
282
283 pub async fn set(self: Vc<Self>, new_version: TraitRef<Box<dyn Version>>) -> Result<()> {
284 let this = self.await?;
285 this.version.set(VersionRef(new_version));
286 Ok(())
287 }
288}