next.js/turbopack/crates/turbo-tasks-fs/src/attach.rs
attach.rs115 lines3.7 KB
use anyhow::Result;
use turbo_rcstr::RcStr;
use turbo_tasks::{ResolvedVc, ValueToString, Vc, turbobail};

use crate::{FileContent, FileMeta, FileSystem, FileSystemPath, LinkContent, RawDirectoryContent};

/// A wrapper [FileSystem] which attaches a child [FileSystem] as a
/// "subdirectory" in the given root [FileSystem].
///
/// Caveat: The `child_path` itself is not visible as a directory entry.
#[derive(ValueToString)]
#[value_to_string("{root_fs}-with-{child_fs}")]
#[turbo_tasks::value]
pub struct AttachedFileSystem {
    root_fs: ResolvedVc<Box<dyn FileSystem>>,
    // we turn this into a string because creating a FileSystemPath requires the filesystem which
    // we are creating (circular reference)
    child_path: RcStr,
    child_fs: ResolvedVc<Box<dyn FileSystem>>,
}

#[turbo_tasks::value_impl]
impl AttachedFileSystem {
    /// Create a new [AttachedFileSystem] which will have the `child_fs` as
    /// an invisible subdirectory of the `child_path`
    #[turbo_tasks::function]
    pub async fn new(
        child_path: FileSystemPath,
        child_fs: ResolvedVc<Box<dyn FileSystem>>,
    ) -> Result<Vc<Self>> {
        Ok(AttachedFileSystem {
            root_fs: child_path.fs,
            child_path: child_path.path.clone(),
            child_fs,
        }
        .cell())
    }

    /// Constructs a [FileSystemPath] of the attachment point referencing
    /// this [AttachedFileSystem]
    #[turbo_tasks::function]
    async fn child_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
        Ok(self.root().await?.join(&self.await?.child_path)?.cell())
    }

    /// Resolves the local path of the root or child filesystem from a path
    /// on the [AttachedFileSystem]
    #[turbo_tasks::function]
    pub async fn get_inner_fs_path(
        self: ResolvedVc<Self>,
        path: FileSystemPath,
    ) -> Result<Vc<FileSystemPath>> {
        let this = self.await?;
        let self_fs: ResolvedVc<Box<dyn FileSystem>> = ResolvedVc::upcast(self);

        if path.fs != self_fs {
            turbobail!(
                "path fs does not match (expected {self_fs}, got {})",
                path.fs
            )
        }

        let child_path = self.child_path().await?;
        Ok(if let Some(inner_path) = child_path.get_path_to(&path) {
            this.child_fs.root().await?.join(inner_path)?.cell()
        } else {
            this.root_fs.root().await?.join(&path.path)?.cell()
        })
    }
}

#[turbo_tasks::value_impl]
impl FileSystem for AttachedFileSystem {
    #[turbo_tasks::function(fs)]
    async fn read(self: Vc<Self>, path: FileSystemPath) -> Result<Vc<FileContent>> {
        Ok(self.get_inner_fs_path(path).await?.read())
    }

    #[turbo_tasks::function(fs)]
    async fn read_link(self: Vc<Self>, path: FileSystemPath) -> Result<Vc<LinkContent>> {
        Ok(self.get_inner_fs_path(path).await?.read_link())
    }

    #[turbo_tasks::function(fs)]
    async fn raw_read_dir(self: Vc<Self>, path: FileSystemPath) -> Result<Vc<RawDirectoryContent>> {
        Ok(self.get_inner_fs_path(path).await?.raw_read_dir())
    }

    #[turbo_tasks::function(fs)]
    async fn write(
        self: Vc<Self>,
        path: FileSystemPath,
        content: Vc<FileContent>,
    ) -> Result<Vc<()>> {
        Ok(self.get_inner_fs_path(path).await?.write(content))
    }

    #[turbo_tasks::function(fs)]
    async fn write_link(
        self: Vc<Self>,
        path: FileSystemPath,
        target: Vc<LinkContent>,
    ) -> Result<Vc<()>> {
        Ok(self
            .get_inner_fs_path(path)
            .await?
            .write_symbolic_link_dir(target))
    }

    #[turbo_tasks::function]
    async fn metadata(self: Vc<Self>, path: FileSystemPath) -> Result<Vc<FileMeta>> {
        Ok(self.get_inner_fs_path(path).await?.metadata())
    }
}
Quest for Codev2.0.0
/
SIGN IN