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#[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 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 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}