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: Vc<FileSystemPath>,
27 child_fs: ResolvedVc<Box<dyn FileSystem>>,
28 ) -> Result<Vc<Self>> {
29 let child_path = child_path.await?;
30
31 Ok(AttachedFileSystem {
32 root_fs: child_path.fs,
33 child_path: child_path.path.clone(),
34 child_fs,
35 }
36 .cell())
37 }
38
39 #[turbo_tasks::function]
44 pub async fn convert_path(
45 self: ResolvedVc<Self>,
46 contained_path_vc: Vc<FileSystemPath>,
47 ) -> Result<Vc<FileSystemPath>> {
48 let contained_path = contained_path_vc.await?;
49 let self_fs: ResolvedVc<Box<dyn FileSystem>> = ResolvedVc::upcast(self);
50 let this = self.await?;
51
52 match contained_path.fs {
53 fs if fs == self_fs => Ok(contained_path_vc),
55 fs if fs == this.root_fs => Ok(self
57 .root()
58 .resolve()
59 .await?
60 .join(contained_path.path.clone())),
61 fs if fs == this.child_fs => Ok(self
63 .child_path()
64 .resolve()
65 .await?
66 .join(contained_path.path.clone())),
67 _ => bail!(
68 "path {} not part of self, the root fs or the child fs",
69 contained_path_vc.to_string().await?
70 ),
71 }
72 }
73
74 #[turbo_tasks::function]
77 async fn child_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
78 Ok(self
79 .root()
80 .resolve()
81 .await?
82 .join(self.await?.child_path.clone()))
83 }
84
85 #[turbo_tasks::function]
88 pub async fn get_inner_fs_path(
89 self: ResolvedVc<Self>,
90 path: Vc<FileSystemPath>,
91 ) -> Result<Vc<FileSystemPath>> {
92 let this = self.await?;
93 let path = path.await?;
94 let self_fs: ResolvedVc<Box<dyn FileSystem>> = ResolvedVc::upcast(self);
95
96 if path.fs != self_fs {
97 let self_fs_str = self_fs.to_string().await?;
98 let path_fs_str = path.fs.to_string().await?;
99 bail!(
100 "path fs does not match (expected {}, got {})",
101 self_fs_str,
102 path_fs_str
103 )
104 }
105
106 let child_path = self.child_path().await?;
107 Ok(if let Some(inner_path) = child_path.get_path_to(&path) {
108 this.child_fs
109 .root()
110 .resolve()
111 .await?
112 .join(inner_path.into())
113 } else {
114 this.root_fs.root().resolve().await?.join(path.path.clone())
115 })
116 }
117}
118
119#[turbo_tasks::value_impl]
120impl FileSystem for AttachedFileSystem {
121 #[turbo_tasks::function(fs)]
122 fn read(self: Vc<Self>, path: Vc<FileSystemPath>) -> Vc<FileContent> {
123 self.get_inner_fs_path(path).read()
124 }
125
126 #[turbo_tasks::function(fs)]
127 fn read_link(self: Vc<Self>, path: Vc<FileSystemPath>) -> Vc<LinkContent> {
128 self.get_inner_fs_path(path).read_link()
129 }
130
131 #[turbo_tasks::function(fs)]
132 fn raw_read_dir(self: Vc<Self>, path: Vc<FileSystemPath>) -> Vc<RawDirectoryContent> {
133 self.get_inner_fs_path(path).raw_read_dir()
134 }
135
136 #[turbo_tasks::function(fs)]
137 fn write(self: Vc<Self>, path: Vc<FileSystemPath>, content: Vc<FileContent>) -> Vc<()> {
138 self.get_inner_fs_path(path).write(content)
139 }
140
141 #[turbo_tasks::function(fs)]
142 fn write_link(self: Vc<Self>, path: Vc<FileSystemPath>, target: Vc<LinkContent>) -> Vc<()> {
143 self.get_inner_fs_path(path).write_link(target)
144 }
145
146 #[turbo_tasks::function]
147 fn metadata(self: Vc<Self>, path: Vc<FileSystemPath>) -> Vc<FileMeta> {
148 self.get_inner_fs_path(path).metadata()
149 }
150}
151
152#[turbo_tasks::value_impl]
153impl ValueToString for AttachedFileSystem {
154 #[turbo_tasks::function]
155 async fn to_string(&self) -> Result<Vc<RcStr>> {
156 let root_fs_str = self.root_fs.to_string().await?;
157 let child_fs_str = self.child_fs.to_string().await?;
158 Ok(Vc::cell(
159 format!("{root_fs_str}-with-{child_fs_str}").into(),
160 ))
161 }
162}