1use crate::{
4 error::VfsErrorKind, FileSystem, SeekAndRead, SeekAndWrite, VfsMetadata, VfsPath, VfsResult,
5};
6
7use std::time::SystemTime;
8
9#[derive(Debug, Clone)]
16pub struct AltrootFS {
17 root: VfsPath,
18}
19
20impl AltrootFS {
21 pub fn new(root: VfsPath) -> Self {
23 AltrootFS { root }
24 }
25}
26
27impl AltrootFS {
28 #[allow(clippy::manual_strip)] fn path(&self, path: &str) -> VfsResult<VfsPath> {
30 if path.is_empty() {
31 return Ok(self.root.clone());
32 }
33 if path.starts_with('/') {
34 return self.root.join(&path[1..]);
35 }
36 self.root.join(path)
37 }
38}
39
40impl FileSystem for AltrootFS {
41 fn read_dir(&self, path: &str) -> VfsResult<Box<dyn Iterator<Item = String> + Send>> {
42 self.path(path)?
43 .read_dir()
44 .map(|result| result.map(|path| path.filename()))
45 .map(|entries| Box::new(entries) as Box<dyn Iterator<Item = String> + Send>)
46 }
47
48 fn create_dir(&self, path: &str) -> VfsResult<()> {
49 self.path(path)?.create_dir()
50 }
51
52 fn open_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndRead + Send>> {
53 self.path(path)?.open_file()
54 }
55
56 fn create_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> {
57 self.path(path)?.create_file()
58 }
59
60 fn append_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> {
61 self.path(path)?.append_file()
62 }
63
64 fn metadata(&self, path: &str) -> VfsResult<VfsMetadata> {
65 self.path(path)?.metadata()
66 }
67
68 fn set_creation_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
69 self.path(path)?.set_creation_time(time)
70 }
71
72 fn set_modification_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
73 self.path(path)?.set_modification_time(time)
74 }
75
76 fn set_access_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
77 self.path(path)?.set_access_time(time)
78 }
79
80 fn exists(&self, path: &str) -> VfsResult<bool> {
81 self.path(path)
82 .map(|path| path.exists())
83 .unwrap_or(Ok(false))
84 }
85
86 fn remove_file(&self, path: &str) -> VfsResult<()> {
87 self.path(path)?.remove_file()
88 }
89
90 fn remove_dir(&self, path: &str) -> VfsResult<()> {
91 self.path(path)?.remove_dir()
92 }
93
94 fn copy_file(&self, src: &str, dest: &str) -> VfsResult<()> {
95 if dest.is_empty() {
96 return Err(VfsErrorKind::NotSupported.into());
97 }
98 self.path(src)?.copy_file(&self.path(dest)?)
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use crate::MemoryFS;
106 test_vfs!({
107 let memory_root: VfsPath = MemoryFS::new().into();
108 let altroot_path = memory_root.join("altroot").unwrap();
109 altroot_path.create_dir().unwrap();
110 AltrootFS::new(altroot_path)
111 });
112
113 #[test]
114 fn parent() {
115 let memory_root: VfsPath = MemoryFS::new().into();
116 let altroot_path = memory_root.join("altroot").unwrap();
117 altroot_path.create_dir().unwrap();
118 let altroot: VfsPath = AltrootFS::new(altroot_path.clone()).into();
119 assert_eq!(altroot.parent(), altroot.root());
120 assert_eq!(altroot_path.parent(), memory_root);
121 }
122}
123
124#[cfg(test)]
125mod tests_physical {
126 use super::*;
127 use crate::PhysicalFS;
128 test_vfs!({
129 let temp_dir = std::env::temp_dir();
130 let dir = temp_dir.join(uuid::Uuid::new_v4().to_string());
131 std::fs::create_dir_all(&dir).unwrap();
132
133 let physical_root: VfsPath = PhysicalFS::new(dir).into();
134 let altroot_path = physical_root.join("altroot").unwrap();
135 altroot_path.create_dir().unwrap();
136 AltrootFS::new(altroot_path)
137 });
138
139 test_vfs_readonly!({
140 let physical_root: VfsPath = PhysicalFS::new("test").into();
141 let altroot_path = physical_root.join("test_directory").unwrap();
142 AltrootFS::new(altroot_path)
143 });
144}