Skip to main content

turbopack_core/
asset.rs

1use anyhow::Result;
2use turbo_rcstr::{RcStr, rcstr};
3use turbo_tasks::{ResolvedVc, Vc};
4use turbo_tasks_fs::{
5    FileContent, FileJsonContent, FileLinesContent, FileSystemPath, LinkContent, LinkType,
6};
7use turbo_tasks_hash::{HashAlgorithm, Xxh3Hash64Hasher};
8
9use crate::version::{VersionedAssetContent, VersionedContent};
10
11/// An asset. It also forms a graph when following [Asset::references].
12#[turbo_tasks::value_trait]
13pub trait Asset {
14    /// The content of the [Asset].
15    #[turbo_tasks::function]
16    fn content(self: Vc<Self>) -> Vc<AssetContent>;
17
18    /// The content of the [Asset] alongside its version.
19    #[turbo_tasks::function]
20    fn versioned_content(self: Vc<Self>) -> Result<Vc<Box<dyn VersionedContent>>> {
21        Ok(Vc::upcast(VersionedAssetContent::new(self.content())))
22    }
23
24    /// Hash of the content of the [Asset]
25    #[turbo_tasks::function]
26    fn content_hash(self: Vc<Self>, algorithm: HashAlgorithm) -> Vc<Option<RcStr>> {
27        self.content().content_hash(algorithm)
28    }
29}
30
31#[turbo_tasks::value(shared)]
32#[derive(Clone)]
33pub enum AssetContent {
34    File(ResolvedVc<FileContent>),
35    // for the relative link, the target is raw value read from the link
36    // for the absolute link, the target is stripped of the root path while reading
37    // See [LinkContent::Link] for more details.
38    Redirect { target: RcStr, link_type: LinkType },
39}
40
41#[turbo_tasks::value_impl]
42impl AssetContent {
43    #[turbo_tasks::function]
44    pub fn file(file: ResolvedVc<FileContent>) -> Result<Vc<Self>> {
45        Ok(AssetContent::File(file).cell())
46    }
47
48    #[turbo_tasks::function]
49    pub fn parse_json(&self) -> Vc<FileJsonContent> {
50        match self {
51            AssetContent::File(content) => content.parse_json(),
52            AssetContent::Redirect { .. } => {
53                FileJsonContent::unparsable(rcstr!("a redirect can't be parsed as json")).cell()
54            }
55        }
56    }
57
58    #[turbo_tasks::function]
59    pub fn file_content(&self) -> Vc<FileContent> {
60        match self {
61            AssetContent::File(content) => **content,
62            AssetContent::Redirect { .. } => FileContent::NotFound.cell(),
63        }
64    }
65
66    #[turbo_tasks::function]
67    pub fn lines(&self) -> Vc<FileLinesContent> {
68        match self {
69            AssetContent::File(content) => content.lines(),
70            AssetContent::Redirect { .. } => FileLinesContent::Unparsable.cell(),
71        }
72    }
73
74    #[turbo_tasks::function]
75    pub fn len(&self) -> Vc<Option<u64>> {
76        match self {
77            AssetContent::File(content) => content.len(),
78            AssetContent::Redirect { .. } => Vc::cell(None),
79        }
80    }
81
82    #[turbo_tasks::function]
83    pub fn parse_json_with_comments(&self) -> Vc<FileJsonContent> {
84        match self {
85            AssetContent::File(content) => content.parse_json_with_comments(),
86            AssetContent::Redirect { .. } => {
87                FileJsonContent::unparsable(rcstr!("a redirect can't be parsed as json")).cell()
88            }
89        }
90    }
91
92    #[turbo_tasks::function]
93    pub async fn write(&self, path: FileSystemPath) -> Result<()> {
94        match self {
95            AssetContent::File(file) => {
96                path.write(**file).as_side_effect().await?;
97            }
98            AssetContent::Redirect { target, link_type } => {
99                path.write_symbolic_link_dir(
100                    LinkContent::Link {
101                        target: target.clone(),
102                        link_type: *link_type,
103                    }
104                    .cell(),
105                )
106                .as_side_effect()
107                .await?;
108            }
109        }
110        Ok(())
111    }
112
113    #[turbo_tasks::function]
114    pub async fn hash(&self) -> Result<Vc<u64>> {
115        match self {
116            AssetContent::File(content) => Ok(content.hash()),
117            AssetContent::Redirect { target, link_type } => {
118                use turbo_tasks_hash::DeterministicHash;
119                let mut hasher = Xxh3Hash64Hasher::new();
120                target.deterministic_hash(&mut hasher);
121                link_type.deterministic_hash(&mut hasher);
122                Ok(Vc::cell(hasher.finish()))
123            }
124        }
125    }
126
127    /// Compared to [AssetContent::hash], this hashes only the bytes of the file content and nothing
128    /// else. If there is no file content, it returns `None`.
129    #[turbo_tasks::function]
130    pub async fn content_hash(&self, algorithm: HashAlgorithm) -> Result<Vc<Option<RcStr>>> {
131        match self {
132            AssetContent::File(content) => Ok(content.content_hash(algorithm)),
133            AssetContent::Redirect { .. } => Ok(Vc::cell(None)),
134        }
135    }
136}