Lints and suggestions for the Nix programming language
at main 109 lines 3.0 kB view raw
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}