turbopack_core/
version.rs1use std::sync::Arc;
2
3use anyhow::{Result, anyhow};
4use turbo_rcstr::RcStr;
5use turbo_tasks::{
6 IntoTraitRef, NonLocalValue, OperationValue, ReadRef, ResolvedVc, State, TraitRef, Vc,
7 debug::ValueDebugFormat, trace::TraceRawVcs,
8};
9use turbo_tasks_fs::{FileContent, LinkType};
10use turbo_tasks_hash::{encode_hex, hash_xxh3_hash64};
11
12use crate::asset::AssetContent;
13
14#[turbo_tasks::value(transparent)]
15pub struct OptionVersionedContent(Option<ResolvedVc<Box<dyn VersionedContent>>>);
16
17#[turbo_tasks::value_trait]
19pub trait VersionedContent {
20 #[turbo_tasks::function]
22 fn content(self: Vc<Self>) -> Vc<AssetContent>;
23
24 #[turbo_tasks::function]
27 fn version(self: Vc<Self>) -> Vc<Box<dyn Version>>;
28
29 #[turbo_tasks::function]
32 async fn update(self: Vc<Self>, from: Vc<Box<dyn Version>>) -> Result<Vc<Update>> {
33 let to = self.version();
37 let from_ref = from.into_trait_ref().await?;
38 let to_ref = to.into_trait_ref().await?;
39
40 if TraitRef::ptr_eq(&from_ref, &to_ref) {
42 return Ok(Update::None.into());
43 }
44
45 let from_id = from.id();
50 let to_id = to.id();
51 let from_id = from_id.await?;
52 let to_id = to_id.await?;
53 Ok(if *from_id == *to_id {
54 Update::None.into()
55 } else {
56 Update::Total(TotalUpdate { to: to_ref }).into()
57 })
58 }
59}
60
61#[turbo_tasks::value]
63pub struct VersionedAssetContent {
64 asset_content: ReadRef<AssetContent>,
69}
70
71#[turbo_tasks::value]
72#[derive(Clone)]
73enum AssetContentSnapshot {
74 File(ReadRef<FileContent>),
75 Redirect { target: String, link_type: LinkType },
76}
77
78#[turbo_tasks::value_impl]
79impl VersionedContent for VersionedAssetContent {
80 #[turbo_tasks::function]
81 fn content(&self) -> Vc<AssetContent> {
82 (*self.asset_content).clone().cell()
83 }
84
85 #[turbo_tasks::function]
86 async fn version(&self) -> Result<Vc<Box<dyn Version>>> {
87 Ok(Vc::upcast(
88 FileHashVersion::compute(&self.asset_content).await?,
89 ))
90 }
91}
92
93#[turbo_tasks::value_impl]
94impl VersionedAssetContent {
95 #[turbo_tasks::function]
96 pub async fn new(asset_content: Vc<AssetContent>) -> Result<Vc<Self>> {
98 let asset_content = asset_content.await?;
99 Ok(Self::cell(VersionedAssetContent { asset_content }))
100 }
101}
102
103impl From<AssetContent> for Vc<VersionedAssetContent> {
104 fn from(asset_content: AssetContent) -> Self {
105 VersionedAssetContent::new(asset_content.cell())
106 }
107}
108
109impl From<AssetContent> for Vc<Box<dyn VersionedContent>> {
110 fn from(asset_content: AssetContent) -> Self {
111 Vc::upcast(VersionedAssetContent::new(asset_content.cell()))
112 }
113}
114
115pub trait VersionedContentExt: Send {
116 fn versioned(self: Vc<Self>) -> Vc<Box<dyn VersionedContent>>;
117}
118
119impl VersionedContentExt for AssetContent {
120 fn versioned(self: Vc<Self>) -> Vc<Box<dyn VersionedContent>> {
121 Vc::upcast(VersionedAssetContent::new(self))
122 }
123}
124
125#[turbo_tasks::value_trait]
130pub trait Version {
131 #[turbo_tasks::function]
135 fn id(self: Vc<Self>) -> Vc<RcStr>;
136}
137
138#[turbo_tasks::value_trait]
144pub trait MergeableVersionedContent: VersionedContent {
145 #[turbo_tasks::function]
146 fn get_merger(self: Vc<Self>) -> Vc<Box<dyn VersionedContentMerger>>;
147}
148
149#[turbo_tasks::value_trait]
152pub trait VersionedContentMerger {
153 #[turbo_tasks::function]
154 fn merge(self: Vc<Self>, contents: Vc<VersionedContents>) -> Vc<Box<dyn VersionedContent>>;
155}
156
157#[turbo_tasks::value(transparent)]
158pub struct VersionedContents(Vec<ResolvedVc<Box<dyn VersionedContent>>>);
159
160#[turbo_tasks::value(operation)]
161pub struct NotFoundVersion;
162
163#[turbo_tasks::value_impl]
164impl NotFoundVersion {
165 #[turbo_tasks::function]
166 pub fn new() -> Vc<Self> {
167 NotFoundVersion.cell()
168 }
169}
170
171#[turbo_tasks::value_impl]
172impl Version for NotFoundVersion {
173 #[turbo_tasks::function]
174 fn id(&self) -> Vc<RcStr> {
175 Vc::cell(Default::default())
176 }
177}
178
179#[turbo_tasks::value(serialization = "none", shared)]
181#[derive(Debug)]
182pub enum Update {
183 Total(TotalUpdate),
186
187 Partial(PartialUpdate),
190
191 Missing,
193
194 None,
196}
197
198#[derive(PartialEq, Eq, Debug, Clone, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
200pub struct TotalUpdate {
201 #[turbo_tasks(trace_ignore)]
207 pub to: TraitRef<Box<dyn Version>>,
208}
209
210#[derive(PartialEq, Eq, Debug, Clone, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
212pub struct PartialUpdate {
213 #[turbo_tasks(trace_ignore)]
216 pub to: TraitRef<Box<dyn Version>>,
217 #[turbo_tasks(trace_ignore)]
220 pub instruction: Arc<serde_json::Value>,
221}
222
223#[turbo_tasks::value(operation)]
226#[derive(Clone)]
227pub struct FileHashVersion {
228 hash: RcStr,
229}
230
231impl FileHashVersion {
232 pub async fn compute(asset_content: &AssetContent) -> Result<Vc<Self>> {
234 match asset_content {
235 AssetContent::File(file_vc) => match &*file_vc.await? {
236 FileContent::Content(file) => {
237 let hash = hash_xxh3_hash64(file.content());
238 let hex_hash = encode_hex(hash);
239 Ok(Self::cell(FileHashVersion {
240 hash: hex_hash.into(),
241 }))
242 }
243 FileContent::NotFound => Err(anyhow!("file not found")),
244 },
245 AssetContent::Redirect { .. } => Err(anyhow!("not a file")),
246 }
247 }
248}
249
250#[turbo_tasks::value_impl]
251impl Version for FileHashVersion {
252 #[turbo_tasks::function]
253 fn id(&self) -> Vc<RcStr> {
254 Vc::cell(self.hash.clone())
255 }
256}
257
258#[derive(Debug, Eq, PartialEq, TraceRawVcs, NonLocalValue, OperationValue)]
261struct VersionRef(
262 #[turbo_tasks(trace_ignore)] TraitRef<Box<dyn Version>>,
266);
267
268#[turbo_tasks::value(serialization = "none")]
269pub struct VersionState {
270 version: State<VersionRef>,
271}
272
273#[turbo_tasks::value_impl]
274impl VersionState {
275 #[turbo_tasks::function]
276 pub fn get(&self) -> Vc<Box<dyn Version>> {
277 TraitRef::cell(self.version.get().0.clone())
278 }
279}
280
281impl VersionState {
282 pub async fn new(version: TraitRef<Box<dyn Version>>) -> Result<Vc<Self>> {
283 Ok(Self::cell(VersionState {
284 version: State::new(VersionRef(version)),
285 }))
286 }
287
288 pub async fn set(self: Vc<Self>, new_version: TraitRef<Box<dyn Version>>) -> Result<()> {
289 let this = self.await?;
290 this.version.set(VersionRef(new_version));
291 Ok(())
292 }
293}