virtual_filesystem/
roc_fs.rs1use crate::file::{DirEntry, File, Metadata, OpenOptions};
2use crate::util::{not_found, not_supported};
3use crate::FileSystem;
4use itertools::Itertools;
5use std::io::ErrorKind;
6
7pub struct RocFS {
11 pub layers: Vec<Box<dyn FileSystem>>,
12}
13
14impl RocFS {
15 pub fn new(layers: Vec<Box<dyn FileSystem>>) -> Self {
21 Self { layers }
22 }
23
24 fn for_each_layer<R, F: Fn(&dyn FileSystem, &str) -> crate::Result<R>>(
30 &self,
31 f: F,
32 path: &str,
33 ) -> crate::Result<R> {
34 for layer in &self.layers {
35 match f(&**layer, path) {
36 Ok(path) => return Ok(path),
37 Err(err) if err.kind() == ErrorKind::NotFound => continue,
38 Err(err) => return Err(err),
39 }
40 }
41
42 Err(not_found())
43 }
44}
45
46impl FileSystem for RocFS {
47 fn create_dir(&self, _path: &str) -> crate::Result<()> {
48 Err(not_supported())
49 }
50
51 fn metadata(&self, path: &str) -> crate::Result<Metadata> {
52 self.for_each_layer(|layer, path| layer.metadata(path), path)
53 }
54
55 fn open_file_options(&self, path: &str, options: &OpenOptions) -> crate::Result<Box<dyn File>> {
56 self.for_each_layer(|layer, path| layer.open_file_options(path, options), path)
57 }
58
59 fn read_dir(
60 &self,
61 path: &str,
62 ) -> crate::Result<Box<dyn Iterator<Item = crate::Result<DirEntry>>>> {
63 Ok(Box::new(
64 self.layers
65 .iter()
66 .map(|layer| layer.read_dir(path))
67 .filter(|res| {
68 res.as_ref()
69 .err()
70 .map(|err| err.kind() != ErrorKind::NotFound)
71 .unwrap_or(true)
72 })
73 .flatten_ok()
74 .try_collect::<_, Vec<_>, _>()?
75 .into_iter(),
76 ))
77 }
78
79 fn remove_dir(&self, _path: &str) -> crate::Result<()> {
80 Err(not_supported())
81 }
82
83 fn remove_file(&self, _path: &str) -> crate::Result<()> {
84 Err(not_supported())
85 }
86}
87
88#[cfg(test)]
89mod test {
90 use crate::file::Metadata;
91 use crate::physical_fs::PhysicalFS;
92 use crate::roc_fs::RocFS;
93 use crate::util::test::read_directory;
94 use crate::FileSystem;
95 use std::io::ErrorKind;
96
97 #[test]
98 fn read_dir_happy_case() {
99 let folder_a = PhysicalFS::new("test/folder_a");
100 let folder_b = PhysicalFS::new("test/folder_b");
101
102 let roc_fs = RocFS::new(vec![Box::new(folder_a), Box::new(folder_b)]);
103 let root = read_directory(&roc_fs, "/");
104
105 itertools::assert_equal(root.keys(), vec!["file_a", "file_b"]);
106 itertools::assert_equal(root.values(), vec![&Metadata::file(6), &Metadata::file(6)])
107 }
108
109 #[test]
110 fn read_dir_missing_folder() {
111 let folder_a = PhysicalFS::new("test/folder_a");
112 let folder_c = PhysicalFS::new("test/folder_c");
113
114 let roc_fs = RocFS::new(vec![Box::new(folder_a), Box::new(folder_c)]);
115 let root = read_directory(&roc_fs, "/");
116
117 itertools::assert_equal(root.keys(), vec!["file_a"]);
118 itertools::assert_equal(root.values(), vec![&Metadata::file(6)])
119 }
120
121 #[test]
122 fn read_dir_missing_folders() {
123 let folder_c = PhysicalFS::new("test/folder_c");
124 let folder_d = PhysicalFS::new("test/folder_d");
125
126 let roc_fs = RocFS::new(vec![Box::new(folder_c), Box::new(folder_d)]);
127 let root = read_directory(&roc_fs, "/");
128
129 assert!(root.is_empty());
130 }
131
132 #[test]
133 fn open_file_happy_case() {
134 let folder_a = PhysicalFS::new("test/folder_a");
135 let folder_b = PhysicalFS::new("test/folder_b");
136
137 let roc_fs = RocFS::new(vec![Box::new(folder_a), Box::new(folder_b)]);
138
139 let file_a = roc_fs
140 .open_file("/file_a")
141 .unwrap()
142 .read_into_string()
143 .unwrap();
144
145 let file_b = roc_fs
146 .open_file("/file_b")
147 .unwrap()
148 .read_into_string()
149 .unwrap();
150
151 assert_eq!(file_a, "file a");
152 assert_eq!(file_b, "file b");
153 }
154
155 #[test]
156 fn open_file_not_found() {
157 let roc_fs = RocFS::new(vec![]);
158
159 let open_res = roc_fs.open_file("abc");
160
161 assert!(open_res.is_err());
162 assert_eq!(open_res.err().unwrap().kind(), ErrorKind::NotFound);
163 }
164}