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#[turbo_tasks::function]
16pub fn no_hash_salt() -> Vc<RcStr> {
17 Vc::cell(RcStr::default())
18}
19
20#[turbo_tasks::value_trait]
29pub trait Asset {
30 #[turbo_tasks::function]
31 fn content(self: Vc<Self>) -> Vc<AssetContent>;
32
33 #[turbo_tasks::function]
35 fn versioned_content(self: Vc<Self>) -> Result<Vc<Box<dyn VersionedContent>>> {
36 Ok(Vc::upcast(VersionedAssetContent::new(self.content())))
37 }
38
39 #[turbo_tasks::function]
42 fn content_hash(
43 self: Vc<Self>,
44 salt: Vc<RcStr>,
45 algorithm: HashAlgorithm,
46 ) -> Vc<Option<RcStr>> {
47 self.content().content_hash(salt, algorithm)
48 }
49}
50
51#[turbo_tasks::value(shared)]
52#[derive(Clone)]
53pub enum AssetContent {
54 File(ResolvedVc<FileContent>),
55 Redirect { target: RcStr, link_type: LinkType },
59}
60
61#[turbo_tasks::value_impl]
62impl AssetContent {
63 #[turbo_tasks::function]
64 pub fn file(file: ResolvedVc<FileContent>) -> Result<Vc<Self>> {
65 Ok(AssetContent::File(file).cell())
66 }
67
68 #[turbo_tasks::function]
69 pub fn parse_json(&self) -> Vc<FileJsonContent> {
70 match self {
71 AssetContent::File(content) => content.parse_json(),
72 AssetContent::Redirect { .. } => {
73 FileJsonContent::unparsable(rcstr!("a redirect can't be parsed as json")).cell()
74 }
75 }
76 }
77
78 #[turbo_tasks::function]
79 pub fn file_content(&self) -> Vc<FileContent> {
80 match self {
81 AssetContent::File(content) => **content,
82 AssetContent::Redirect { .. } => FileContent::NotFound.cell(),
83 }
84 }
85
86 #[turbo_tasks::function]
87 pub fn lines(&self) -> Vc<FileLinesContent> {
88 match self {
89 AssetContent::File(content) => content.lines(),
90 AssetContent::Redirect { .. } => FileLinesContent::Unparsable.cell(),
91 }
92 }
93
94 #[turbo_tasks::function]
95 pub fn len(&self) -> Vc<Option<u64>> {
96 match self {
97 AssetContent::File(content) => content.len(),
98 AssetContent::Redirect { .. } => Vc::cell(None),
99 }
100 }
101
102 #[turbo_tasks::function]
103 pub fn parse_json_with_comments(&self) -> Vc<FileJsonContent> {
104 match self {
105 AssetContent::File(content) => content.parse_json_with_comments(),
106 AssetContent::Redirect { .. } => {
107 FileJsonContent::unparsable(rcstr!("a redirect can't be parsed as json")).cell()
108 }
109 }
110 }
111
112 #[turbo_tasks::function]
113 pub async fn write(&self, path: FileSystemPath) -> Result<()> {
114 match self {
115 AssetContent::File(file) => {
116 path.write(**file).as_side_effect().await?;
117 }
118 AssetContent::Redirect { target, link_type } => {
119 path.write_symbolic_link_dir(
120 LinkContent::Link {
121 target: target.clone(),
122 link_type: *link_type,
123 }
124 .cell(),
125 )
126 .as_side_effect()
127 .await?;
128 }
129 }
130 Ok(())
131 }
132
133 #[turbo_tasks::function]
134 pub async fn hash(&self) -> Result<Vc<u64>> {
135 match self {
136 AssetContent::File(content) => Ok(content.hash()),
137 AssetContent::Redirect { target, link_type } => {
138 use turbo_tasks_hash::DeterministicHash;
139 let mut hasher = Xxh3Hash64Hasher::new();
140 target.deterministic_hash(&mut hasher);
141 link_type.deterministic_hash(&mut hasher);
142 Ok(Vc::cell(hasher.finish()))
143 }
144 }
145 }
146
147 #[turbo_tasks::function]
153 pub async fn content_hash(
154 &self,
155 salt: Vc<RcStr>,
156 algorithm: HashAlgorithm,
157 ) -> Result<Vc<Option<RcStr>>> {
158 match self {
159 AssetContent::File(content) => Ok(content.content_hash(salt, algorithm)),
160 AssetContent::Redirect { .. } => Ok(Vc::cell(None)),
161 }
162 }
163}