Skip to main content

turbo_tasks_fs/
attach.rs

1use anyhow::Result;
2use turbo_rcstr::RcStr;
3use turbo_tasks::{ResolvedVc, ValueToString, Vc, turbobail};
4
5use crate::{FileContent, FileMeta, FileSystem, FileSystemPath, LinkContent, RawDirectoryContent};
6
7/// A wrapper [FileSystem] which attaches a child [FileSystem] as a
8/// "subdirectory" in the given root [FileSystem].
9///
10/// Caveat: The `child_path` itself is not visible as a directory entry.
11#[derive(ValueToString)]
12#[value_to_string("{root_fs}-with-{child_fs}")]
13#[turbo_tasks::value]
14pub struct AttachedFileSystem {
15    root_fs: ResolvedVc<Box<dyn FileSystem>>,
16    // we turn this into a string because creating a FileSystemPath requires the filesystem which
17    // we are creating (circular reference)
18    child_path: RcStr,
19    child_fs: ResolvedVc<Box<dyn FileSystem>>,
20}
21
22#[turbo_tasks::value_impl]
23impl AttachedFileSystem {
24    /// Create a new [AttachedFileSystem] which will have the `child_fs` as
25    /// an invisible subdirectory of the `child_path`
26    #[turbo_tasks::function]
27    pub async fn new(
28        child_path: FileSystemPath,
29        child_fs: ResolvedVc<Box<dyn FileSystem>>,
30    ) -> Result<Vc<Self>> {
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    /// Constructs a [FileSystemPath] of the attachment point referencing
40    /// this [AttachedFileSystem]
41    #[turbo_tasks::function]
42    async fn child_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
43        Ok(self.root().await?.join(&self.await?.child_path)?.cell())
44    }
45
46    /// Resolves the local path of the root or child filesystem from a path
47    /// on the [AttachedFileSystem]
48    #[turbo_tasks::function]
49    pub async fn get_inner_fs_path(
50        self: ResolvedVc<Self>,
51        path: FileSystemPath,
52    ) -> Result<Vc<FileSystemPath>> {
53        let this = self.await?;
54        let self_fs: ResolvedVc<Box<dyn FileSystem>> = ResolvedVc::upcast(self);
55
56        if path.fs != self_fs {
57            turbobail!(
58                "path fs does not match (expected {self_fs}, got {})",
59                path.fs
60            )
61        }
62
63        let child_path = self.child_path().await?;
64        Ok(if let Some(inner_path) = child_path.get_path_to(&path) {
65            this.child_fs.root().await?.join(inner_path)?.cell()
66        } else {
67            this.root_fs.root().await?.join(&path.path)?.cell()
68        })
69    }
70}
71
72#[turbo_tasks::value_impl]
73impl FileSystem for AttachedFileSystem {
74    #[turbo_tasks::function(fs)]
75    async fn read(self: Vc<Self>, path: FileSystemPath) -> Result<Vc<FileContent>> {
76        Ok(self.get_inner_fs_path(path).await?.read())
77    }
78
79    #[turbo_tasks::function(fs)]
80    async fn read_link(self: Vc<Self>, path: FileSystemPath) -> Result<Vc<LinkContent>> {
81        Ok(self.get_inner_fs_path(path).await?.read_link())
82    }
83
84    #[turbo_tasks::function(fs)]
85    async fn raw_read_dir(self: Vc<Self>, path: FileSystemPath) -> Result<Vc<RawDirectoryContent>> {
86        Ok(self.get_inner_fs_path(path).await?.raw_read_dir())
87    }
88
89    #[turbo_tasks::function(fs)]
90    async fn write(
91        self: Vc<Self>,
92        path: FileSystemPath,
93        content: Vc<FileContent>,
94    ) -> Result<Vc<()>> {
95        Ok(self.get_inner_fs_path(path).await?.write(content))
96    }
97
98    #[turbo_tasks::function(fs)]
99    async fn write_link(
100        self: Vc<Self>,
101        path: FileSystemPath,
102        target: Vc<LinkContent>,
103    ) -> Result<Vc<()>> {
104        Ok(self
105            .get_inner_fs_path(path)
106            .await?
107            .write_symbolic_link_dir(target))
108    }
109
110    #[turbo_tasks::function]
111    async fn metadata(self: Vc<Self>, path: FileSystemPath) -> Result<Vc<FileMeta>> {
112        Ok(self.get_inner_fs_path(path).await?.metadata())
113    }
114}