1use anyhow::Result;
2use turbo_rcstr::{RcStr, rcstr};
3use turbo_tasks::{ResolvedVc, Vc};
4use turbo_tasks_fs::{
5 FileContent, FileJsonContent, FileLinesContent, FileSystemPath, WriteLinkContent,
6};
7use turbo_tasks_hash::{HashAlgorithm, deterministic_hash};
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(WriteLinkContent),
57}
58
59#[turbo_tasks::value_impl]
60impl AssetContent {
61 #[turbo_tasks::function]
62 pub fn file(file: ResolvedVc<FileContent>) -> Result<Vc<Self>> {
63 Ok(AssetContent::File(file).cell())
64 }
65
66 #[turbo_tasks::function]
67 pub fn parse_json(&self) -> Vc<FileJsonContent> {
68 match self {
69 AssetContent::File(content) => content.parse_json(),
70 AssetContent::Redirect(..) => {
71 FileJsonContent::unparsable(rcstr!("a redirect can't be parsed as json")).cell()
72 }
73 }
74 }
75
76 #[turbo_tasks::function]
77 pub fn file_content(&self) -> Vc<FileContent> {
78 match self {
79 AssetContent::File(content) => **content,
80 AssetContent::Redirect(..) => FileContent::NotFound.cell(),
81 }
82 }
83
84 #[turbo_tasks::function]
85 pub fn lines(&self) -> Vc<FileLinesContent> {
86 match self {
87 AssetContent::File(content) => content.lines(),
88 AssetContent::Redirect(..) => FileLinesContent::Unparsable.cell(),
89 }
90 }
91
92 #[turbo_tasks::function]
93 pub fn len(&self) -> Vc<Option<u64>> {
94 match self {
95 AssetContent::File(content) => content.len(),
96 AssetContent::Redirect(..) => Vc::cell(None),
97 }
98 }
99
100 #[turbo_tasks::function]
101 pub fn parse_json_with_comments(&self) -> Vc<FileJsonContent> {
102 match self {
103 AssetContent::File(content) => content.parse_json_with_comments(),
104 AssetContent::Redirect(..) => {
105 FileJsonContent::unparsable(rcstr!("a redirect can't be parsed as json")).cell()
106 }
107 }
108 }
109
110 #[turbo_tasks::function]
111 pub async fn write(&self, path: FileSystemPath) -> Result<()> {
112 match self {
113 AssetContent::File(file) => {
114 path.write(**file).as_side_effect().await?;
115 }
116 AssetContent::Redirect(content) => {
117 path.write_link(content.clone().cell())
118 .as_side_effect()
119 .await?;
120 }
121 }
122 Ok(())
123 }
124
125 #[turbo_tasks::function]
126 pub async fn hash(&self, salt: Vc<RcStr>, algorithm: HashAlgorithm) -> Result<Vc<RcStr>> {
127 Ok(match self {
128 AssetContent::File(content) => content.hash(salt, algorithm),
129 AssetContent::Redirect(content) => Vc::cell(RcStr::from(
130 deterministic_hash(&salt.await?, content, algorithm),
132 )),
133 })
134 }
135
136 #[turbo_tasks::function]
142 pub async fn content_hash(
143 &self,
144 salt: Vc<RcStr>,
145 algorithm: HashAlgorithm,
146 ) -> Result<Vc<Option<RcStr>>> {
147 match self {
148 AssetContent::File(content) => Ok(content.content_hash(salt, algorithm)),
149 AssetContent::Redirect(..) => Ok(Vc::cell(None)),
150 }
151 }
152}