Lints and suggestions for the Nix programming language
1use std::{
2 collections::HashMap,
3 default::Default,
4 path::{Path, PathBuf},
5};
6
7use indexmap::IndexSet;
8use rayon::prelude::*;
9
10#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
11pub struct FileId(pub usize);
12
13#[derive(Debug, Default)]
14pub struct Interner {
15 map: IndexSet<PathBuf>,
16}
17
18impl Interner {
19 pub fn get<P: AsRef<Path>>(&self, path: P) -> Option<FileId> {
20 self.map.get_index_of(path.as_ref()).map(FileId)
21 }
22 pub fn intern(&mut self, path: PathBuf) -> FileId {
23 let (id, _) = self.map.insert_full(path);
24 FileId(id)
25 }
26 pub fn lookup(&self, file: FileId) -> Option<&Path> {
27 self.map.get_index(file.0).map(PathBuf::as_path)
28 }
29}
30
31#[derive(Default)]
32pub struct ReadOnlyVfs {
33 interner: Interner,
34 data: HashMap<FileId, Vec<u8>>,
35}
36
37impl ReadOnlyVfs {
38 pub fn singleton<P: AsRef<Path>>(path: P, contents: &[u8]) -> Self {
39 let mut vfs = ReadOnlyVfs::default();
40 vfs.set_file_contents(path, contents);
41 vfs
42 }
43 pub fn alloc_file_id<P: AsRef<Path>>(&mut self, path: P) -> FileId {
44 self.interner.intern(path.as_ref().to_owned())
45 }
46 #[must_use]
47 pub fn len(&self) -> usize {
48 self.data.len()
49 }
50 #[must_use]
51 pub fn is_empty(&self) -> bool {
52 self.data.is_empty()
53 }
54 #[must_use]
55 pub fn file_path(&self, file_id: FileId) -> &Path {
56 self.interner.lookup(file_id).unwrap()
57 }
58 #[must_use]
59 pub fn get(&self, file_id: FileId) -> &Vec<u8> {
60 self.data.get(&file_id).unwrap()
61 }
62 #[must_use]
63 pub fn get_str(&self, file_id: FileId) -> &str {
64 std::str::from_utf8(self.get(file_id)).unwrap()
65 }
66 pub fn get_mut(&mut self, file_id: FileId) -> &mut Vec<u8> {
67 self.data.get_mut(&file_id).unwrap()
68 }
69 pub fn set_file_contents<P: AsRef<Path>>(&mut self, path: P, contents: &[u8]) {
70 let file_id = self.alloc_file_id(path);
71 self.data.insert(file_id, contents.to_owned());
72 }
73 pub fn iter(&self) -> impl Iterator<Item = VfsEntry<'_>> {
74 self.data.keys().map(move |file_id| VfsEntry {
75 file_id: *file_id,
76 file_path: self.file_path(*file_id),
77 contents: self.get_str(*file_id),
78 })
79 }
80 #[must_use]
81 pub fn par_iter(&self) -> impl ParallelIterator<Item = VfsEntry<'_>> {
82 self.data.par_iter().map(move |(file_id, _)| VfsEntry {
83 file_id: *file_id,
84 file_path: self.file_path(*file_id),
85 contents: self.get_str(*file_id),
86 })
87 }
88}
89
90pub struct VfsEntry<'ρ> {
91 pub file_id: FileId,
92 pub file_path: &'ρ Path,
93 pub contents: &'ρ str,
94}
95
96#[cfg(test)]
97mod test {
98 use super::*;
99
100 #[test]
101 fn trivial() {
102 let mut vfs = ReadOnlyVfs::default();
103 let f1 = "a/b/c";
104 let id1 = vfs.alloc_file_id(f1);
105 let data = "hello".as_bytes().to_vec();
106 vfs.set_file_contents(f1, &data);
107 assert_eq!(vfs.get(id1), &data);
108 }
109}