diff options
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | vfs/Cargo.toml | 8 | ||||
-rw-r--r-- | vfs/src/lib.rs | 89 |
3 files changed, 99 insertions, 1 deletions
@@ -3,6 +3,7 @@ | |||
3 | members = [ | 3 | members = [ |
4 | "bin", | 4 | "bin", |
5 | "lib", | 5 | "lib", |
6 | "macros" | 6 | "macros", |
7 | "vfs" | ||
7 | ] | 8 | ] |
8 | 9 | ||
diff --git a/vfs/Cargo.toml b/vfs/Cargo.toml new file mode 100644 index 0000000..56a2c61 --- /dev/null +++ b/vfs/Cargo.toml | |||
@@ -0,0 +1,8 @@ | |||
1 | [package] | ||
2 | name = "vfs" | ||
3 | version = "0.1.0" | ||
4 | edition = "2018" | ||
5 | |||
6 | [dependencies] | ||
7 | indexmap = "1.6.2" | ||
8 | |||
diff --git a/vfs/src/lib.rs b/vfs/src/lib.rs new file mode 100644 index 0000000..f620ab9 --- /dev/null +++ b/vfs/src/lib.rs | |||
@@ -0,0 +1,89 @@ | |||
1 | use std::{ | ||
2 | collections::HashMap, | ||
3 | default::Default, | ||
4 | path::{Path, PathBuf}, | ||
5 | }; | ||
6 | |||
7 | use indexmap::IndexSet; | ||
8 | |||
9 | #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)] | ||
10 | pub struct FileId(u32); | ||
11 | |||
12 | #[derive(Debug, Default)] | ||
13 | pub struct Interner { | ||
14 | map: IndexSet<PathBuf>, | ||
15 | } | ||
16 | |||
17 | impl Interner { | ||
18 | pub fn get<P: AsRef<Path>>(&self, path: P) -> Option<FileId> { | ||
19 | self.map | ||
20 | .get_index_of(path.as_ref()) | ||
21 | .map(|i| FileId(i as u32)) | ||
22 | } | ||
23 | pub fn intern(&mut self, path: PathBuf) -> FileId { | ||
24 | let (id, _) = self.map.insert_full(path); | ||
25 | FileId(id as u32) | ||
26 | } | ||
27 | pub fn lookup(&self, file: FileId) -> Option<&Path> { | ||
28 | self.map.get_index(file.0 as usize).map(|p| p.as_path()) | ||
29 | } | ||
30 | } | ||
31 | |||
32 | #[derive(Default)] | ||
33 | pub struct ReadOnlyVfs { | ||
34 | interner: Interner, | ||
35 | data: HashMap<FileId, Vec<u8>>, | ||
36 | } | ||
37 | |||
38 | impl ReadOnlyVfs { | ||
39 | pub fn alloc_file_id<P: AsRef<Path>>(&mut self, path: P) -> FileId { | ||
40 | self.interner.intern(path.as_ref().to_owned()) | ||
41 | } | ||
42 | pub fn len(&self) -> usize { | ||
43 | self.data.len() | ||
44 | } | ||
45 | pub fn file_path(&self, file_id: FileId) -> &Path { | ||
46 | self.interner.lookup(file_id).unwrap() | ||
47 | } | ||
48 | pub fn get(&self, file_id: FileId) -> &Vec<u8> { | ||
49 | self.data.get(&file_id).unwrap() | ||
50 | } | ||
51 | pub fn get_str(&self, file_id: FileId) -> &str { | ||
52 | std::str::from_utf8(self.get(file_id)).unwrap() | ||
53 | } | ||
54 | pub fn get_mut(&mut self, file_id: FileId) -> &mut Vec<u8> { | ||
55 | self.data.get_mut(&file_id).unwrap() | ||
56 | } | ||
57 | pub fn set_file_contents<P: AsRef<Path>>(&mut self, path: P, contents: &[u8]) { | ||
58 | let file_id = self.alloc_file_id(path); | ||
59 | self.data.insert(file_id, contents.to_owned()); | ||
60 | } | ||
61 | pub fn iter<'ρ>(&'ρ self) -> impl Iterator<Item = VfsEntry<'ρ>> { | ||
62 | self.data.iter().map(move |(file_id, _)| VfsEntry { | ||
63 | file_id: *file_id, | ||
64 | file_path: self.file_path(*file_id), | ||
65 | contents: self.get_str(*file_id), | ||
66 | }) | ||
67 | } | ||
68 | } | ||
69 | |||
70 | pub struct VfsEntry<'ρ> { | ||
71 | pub file_id: FileId, | ||
72 | pub file_path: &'ρ Path, | ||
73 | pub contents: &'ρ str, | ||
74 | } | ||
75 | |||
76 | #[cfg(test)] | ||
77 | mod test { | ||
78 | use super::*; | ||
79 | |||
80 | #[test] | ||
81 | fn trivial() { | ||
82 | let mut vfs = ReadOnlyVfs::default(); | ||
83 | let f1 = "a/b/c"; | ||
84 | let id1 = vfs.alloc_file_id(f1); | ||
85 | let data = "hello".as_bytes().to_vec(); | ||
86 | vfs.set_file_contents(f1, &data); | ||
87 | assert_eq!(vfs.get(id1), &data); | ||
88 | } | ||
89 | } | ||