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 fn content(self: Vc<Self>) -> Vc<AssetContent>;
22
23 fn version(self: Vc<Self>) -> Vc<Box<dyn Version>>;
26
27 async fn update(self: Vc<Self>, from: Vc<Box<dyn Version>>) -> Result<Vc<Update>> {
30 let to = self.version();
34 let from_ref = from.into_trait_ref().await?;
35 let to_ref = to.into_trait_ref().await?;
36
37 if TraitRef::ptr_eq(&from_ref, &to_ref) {
39 return Ok(Update::None.into());
40 }
41
42 let from_id = from.id();
47 let to_id = to.id();
48 let from_id = from_id.await?;
49 let to_id = to_id.await?;
50 Ok(if *from_id == *to_id {
51 Update::None.into()
52 } else {
53 Update::Total(TotalUpdate { to: to_ref }).into()
54 })
55 }
56}
57
58#[turbo_tasks::value]
60pub struct VersionedAssetContent {
61 asset_content: ReadRef<AssetContent>,
66}
67
68#[turbo_tasks::value]
69#[derive(Clone)]
70enum AssetContentSnapshot {
71 File(ReadRef<FileContent>),
72 Redirect { target: String, link_type: LinkType },
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 fn id(self: Vc<Self>) -> Vc<RcStr>;
132}
133
134#[turbo_tasks::value_trait]
140pub trait MergeableVersionedContent: VersionedContent {
141 fn get_merger(self: Vc<Self>) -> Vc<Box<dyn VersionedContentMerger>>;
142}
143
144#[turbo_tasks::value_trait]
147pub trait VersionedContentMerger {
148 fn merge(self: Vc<Self>, contents: Vc<VersionedContents>) -> Vc<Box<dyn VersionedContent>>;
149}
150
151#[turbo_tasks::value(transparent)]
152pub struct VersionedContents(Vec<ResolvedVc<Box<dyn VersionedContent>>>);
153
154#[turbo_tasks::value(operation)]
155pub struct NotFoundVersion;
156
157#[turbo_tasks::value_impl]
158impl NotFoundVersion {
159 #[turbo_tasks::function]
160 pub fn new() -> Vc<Self> {
161 NotFoundVersion.cell()
162 }
163}
164
165#[turbo_tasks::value_impl]
166impl Version for NotFoundVersion {
167 #[turbo_tasks::function]
168 fn id(&self) -> Vc<RcStr> {
169 Vc::cell(Default::default())
170 }
171}
172
173#[turbo_tasks::value(serialization = "none", shared)]
175#[derive(Debug)]
176pub enum Update {
177 Total(TotalUpdate),
180
181 Partial(PartialUpdate),
184
185 Missing,
187
188 None,
190}
191
192#[derive(PartialEq, Eq, Debug, Clone, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
194pub struct TotalUpdate {
195 #[turbo_tasks(trace_ignore)]
201 pub to: TraitRef<Box<dyn Version>>,
202}
203
204#[derive(PartialEq, Eq, Debug, Clone, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
206pub struct PartialUpdate {
207 #[turbo_tasks(trace_ignore)]
210 pub to: TraitRef<Box<dyn Version>>,
211 #[turbo_tasks(trace_ignore)]
214 pub instruction: Arc<serde_json::Value>,
215}
216
217#[turbo_tasks::value(operation)]
220#[derive(Clone)]
221pub struct FileHashVersion {
222 hash: RcStr,
223}
224
225impl FileHashVersion {
226 pub async fn compute(asset_content: &AssetContent) -> Result<Vc<Self>> {
228 match asset_content {
229 AssetContent::File(file_vc) => match &*file_vc.await? {
230 FileContent::Content(file) => {
231 let hash = hash_xxh3_hash64(file.content());
232 let hex_hash = encode_hex(hash);
233 Ok(Self::cell(FileHashVersion {
234 hash: hex_hash.into(),
235 }))
236 }
237 FileContent::NotFound => Err(anyhow!("file not found")),
238 },
239 AssetContent::Redirect { .. } => Err(anyhow!("not a file")),
240 }
241 }
242}
243
244#[turbo_tasks::value_impl]
245impl Version for FileHashVersion {
246 #[turbo_tasks::function]
247 fn id(&self) -> Vc<RcStr> {
248 Vc::cell(self.hash.clone())
249 }
250}
251
252#[derive(Debug, Eq, PartialEq, TraceRawVcs, NonLocalValue, OperationValue)]
255struct VersionRef(
256 #[turbo_tasks(trace_ignore)] TraitRef<Box<dyn Version>>,
260);
261
262#[turbo_tasks::value(serialization = "none")]
263pub struct VersionState {
264 version: State<VersionRef>,
265}
266
267#[turbo_tasks::value_impl]
268impl VersionState {
269 #[turbo_tasks::function]
270 pub fn get(&self) -> Vc<Box<dyn Version>> {
271 TraitRef::cell(self.version.get().0.clone())
272 }
273}
274
275impl VersionState {
276 pub async fn new(version: TraitRef<Box<dyn Version>>) -> Result<Vc<Self>> {
277 Ok(Self::cell(VersionState {
278 version: State::new(VersionRef(version)),
279 }))
280 }
281
282 pub async fn set(self: Vc<Self>, new_version: TraitRef<Box<dyn Version>>) -> Result<()> {
283 let this = self.await?;
284 this.version.set(VersionRef(new_version));
285 Ok(())
286 }
287}