1use anyhow::{Result, bail};
2use turbo_rcstr::RcStr;
3use turbo_tasks::{ResolvedVc, ValueToString, Vc};
4
5use crate::{FileContent, FileMeta, FileSystem, FileSystemPath, LinkContent, RawDirectoryContent};
6
7#[turbo_tasks::value]
12pub struct AttachedFileSystem {
13 root_fs: ResolvedVc<Box<dyn FileSystem>>,
14 child_path: RcStr,
17 child_fs: ResolvedVc<Box<dyn FileSystem>>,
18}
19
20#[turbo_tasks::value_impl]
21impl AttachedFileSystem {
22 #[turbo_tasks::function]
25 pub async fn new(
26 child_path: FileSystemPath,
27 child_fs: ResolvedVc<Box<dyn FileSystem>>,
28 ) -> Result<Vc<Self>> {
29 Ok(AttachedFileSystem {
30 root_fs: child_path.fs,
31 child_path: child_path.path.clone(),
32 child_fs,
33 }
34 .cell())
35 }
36
37 #[turbo_tasks::function]
42 pub async fn convert_path(
43 self: ResolvedVc<Self>,
44 contained_path: FileSystemPath,
45 ) -> Result<Vc<FileSystemPath>> {
46 let self_fs: ResolvedVc<Box<dyn FileSystem>> = ResolvedVc::upcast(self);
47 let this = self.await?;
48
49 match contained_path.fs {
50 fs if fs == self_fs => Ok(contained_path.cell()),
52 fs if fs == this.root_fs => Ok(self.root().await?.join(&contained_path.path)?.cell()),
54 fs if fs == this.child_fs => {
56 Ok(self.child_path().await?.join(&contained_path.path)?.cell())
57 }
58 _ => bail!(
59 "path {} not part of self, the root fs or the child fs",
60 contained_path.value_to_string().await?
61 ),
62 }
63 }
64
65 #[turbo_tasks::function]
68 async fn child_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
69 Ok(self.root().await?.join(&self.await?.child_path)?.cell())
70 }
71
72 #[turbo_tasks::function]
75 pub async fn get_inner_fs_path(
76 self: ResolvedVc<Self>,
77 path: FileSystemPath,
78 ) -> Result<Vc<FileSystemPath>> {
79 let this = self.await?;
80 let self_fs: ResolvedVc<Box<dyn FileSystem>> = ResolvedVc::upcast(self);
81
82 if path.fs != self_fs {
83 let self_fs_str = self_fs.to_string().await?;
84 let path_fs_str = path.fs.to_string().await?;
85 bail!(
86 "path fs does not match (expected {}, got {})",
87 self_fs_str,
88 path_fs_str
89 )
90 }
91
92 let child_path = self.child_path().await?;
93 Ok(if let Some(inner_path) = child_path.get_path_to(&path) {
94 this.child_fs.root().await?.join(inner_path)?.cell()
95 } else {
96 this.root_fs.root().await?.join(&path.path)?.cell()
97 })
98 }
99}
100
101#[turbo_tasks::value_impl]
102impl FileSystem for AttachedFileSystem {
103 #[turbo_tasks::function(fs)]
104 async fn read(self: Vc<Self>, path: FileSystemPath) -> Result<Vc<FileContent>> {
105 Ok(self.get_inner_fs_path(path).await?.read())
106 }
107
108 #[turbo_tasks::function(fs)]
109 async fn read_link(self: Vc<Self>, path: FileSystemPath) -> Result<Vc<LinkContent>> {
110 Ok(self.get_inner_fs_path(path).await?.read_link())
111 }
112
113 #[turbo_tasks::function(fs)]
114 async fn raw_read_dir(self: Vc<Self>, path: FileSystemPath) -> Result<Vc<RawDirectoryContent>> {
115 Ok(self.get_inner_fs_path(path).await?.raw_read_dir())
116 }
117
118 #[turbo_tasks::function(fs)]
119 async fn write(
120 self: Vc<Self>,
121 path: FileSystemPath,
122 content: Vc<FileContent>,
123 ) -> Result<Vc<()>> {
124 Ok(self.get_inner_fs_path(path).await?.write(content))
125 }
126
127 #[turbo_tasks::function(fs)]
128 async fn write_link(
129 self: Vc<Self>,
130 path: FileSystemPath,
131 target: Vc<LinkContent>,
132 ) -> Result<Vc<()>> {
133 Ok(self.get_inner_fs_path(path).await?.write_link(target))
134 }
135
136 #[turbo_tasks::function]
137 async fn metadata(self: Vc<Self>, path: FileSystemPath) -> Result<Vc<FileMeta>> {
138 Ok(self.get_inner_fs_path(path).await?.metadata())
139 }
140}
141
142#[turbo_tasks::value_impl]
143impl ValueToString for AttachedFileSystem {
144 #[turbo_tasks::function]
145 async fn to_string(&self) -> Result<Vc<RcStr>> {
146 let root_fs_str = self.root_fs.to_string().await?;
147 let child_fs_str = self.child_fs.to_string().await?;
148 Ok(Vc::cell(
149 format!("{root_fs_str}-with-{child_fs_str}").into(),
150 ))
151 }
152}