Skip to main content

turbo_tasks_fs/
fs_map.rs

1use std::{
2    collections::BTreeMap,
3    ops::Bound,
4    path::{Path, PathBuf},
5};
6
7use turbo_rcstr::RcStr;
8use turbo_tasks::{OperationVc, ResolvedVc, Vc};
9use turbo_unix_path::sys_to_unix;
10
11use crate::{DiskFileSystem, FileSystemPath};
12
13/// An ordered set of canonical system roots and their owning filesystems.
14///
15/// The roots must not overlap: no root may be an ancestor of another root. [`Self::lookup`]
16/// relies on this invariant when selecting the nearest preceding root in path order.
17#[turbo_tasks::value(shared)]
18pub struct DiskFileSystemMap(BTreeMap<PathBuf, ResolvedVc<DiskFileSystem>>);
19
20impl FromIterator<(PathBuf, ResolvedVc<DiskFileSystem>)> for DiskFileSystemMap {
21    fn from_iter<T: IntoIterator<Item = (PathBuf, ResolvedVc<DiskFileSystem>)>>(iter: T) -> Self {
22        let filesystems = BTreeMap::from_iter(iter);
23        let mut map = DiskFileSystemMap(BTreeMap::new());
24        for (root, fs) in filesystems {
25            assert!(
26                map.lookup(&root).is_none(),
27                "filesystem root {} overlaps another filesystem root",
28                root.display()
29            );
30            map.0.insert(root, fs);
31        }
32        map
33    }
34}
35
36impl DiskFileSystemMap {
37    pub fn has_file_system_other_than(&self, current: ResolvedVc<DiskFileSystem>) -> bool {
38        self.0.values().any(|file_system| *file_system != current)
39    }
40
41    /// Converts an absolute system path into a path owned by one of the installed filesystems.
42    ///
43    /// Returns `None` if the file path does not exist inside any other root, or if the relative
44    /// path would not be valid unicode.
45    pub fn lookup(&self, path: &Path) -> Option<FileSystemPath> {
46        let (root, fs) = self.0.upper_bound(Bound::Included(path)).peek_prev()?;
47        let relative = path.strip_prefix(root).ok()?.to_str()?;
48        Some(FileSystemPath::new_normalized_unchecked(
49            ResolvedVc::upcast(*fs),
50            RcStr::from(sys_to_unix(relative)),
51        ))
52    }
53
54    /// Creates a new empty `DiskFileSystemMap`, used when constructing a [`DiskFileSystem`] that
55    /// cannot traverse to any other roots outside of itself.
56    pub fn empty() -> OperationVc<DiskFileSystemMap> {
57        #[turbo_tasks::function(operation)]
58        pub fn operation() -> Vc<DiskFileSystemMap> {
59            DiskFileSystemMap(BTreeMap::new()).cell()
60        }
61        operation()
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use turbo_rcstr::rcstr;
68    use turbo_tasks_backend::{BackendOptions, TurboTasksBackend, noop_backing_storage};
69
70    use super::*;
71
72    #[tokio::test]
73    async fn component_safe_lookup() {
74        #[turbo_tasks::function(operation, root)]
75        async fn assert_component_safe_lookup() -> anyhow::Result<()> {
76            let fs = DiskFileSystem::new(rcstr!("root"), Vc::cell(rcstr!("/tmp/root")))
77                .to_resolved()
78                .await?;
79            let map: DiskFileSystemMap = [(PathBuf::from("/tmp/root"), fs)].into_iter().collect();
80            assert!(!map.has_file_system_other_than(fs));
81            assert_eq!(
82                map.lookup(Path::new("/tmp/root/file")).unwrap().path,
83                "file"
84            );
85            assert!(map.lookup(Path::new("/tmp/root-other/file")).is_none());
86            Ok(())
87        }
88
89        let tt = turbo_tasks::TurboTasks::new(TurboTasksBackend::new(
90            BackendOptions::default(),
91            noop_backing_storage(),
92        ));
93        tt.run_once(async {
94            assert_component_safe_lookup()
95                .read_strongly_consistent()
96                .await
97        })
98        .await
99        .unwrap();
100    }
101}