aboutsummaryrefslogtreecommitdiff
path: root/vfs/src/lib.rs
blob: 8b5df79688fe855e7841d1e4b7b9c379d388df9a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use std::{
    collections::HashMap,
    default::Default,
    path::{Path, PathBuf},
};

use indexmap::IndexSet;

#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct FileId(u32);

#[derive(Debug, Default)]
pub struct Interner {
    map: IndexSet<PathBuf>,
}

impl Interner {
    pub fn get<P: AsRef<Path>>(&self, path: P) -> Option<FileId> {
        self.map
            .get_index_of(path.as_ref())
            .map(|i| FileId(i as u32))
    }
    pub fn intern(&mut self, path: PathBuf) -> FileId {
        let (id, _) = self.map.insert_full(path);
        FileId(id as u32)
    }
    pub fn lookup(&self, file: FileId) -> Option<&Path> {
        self.map.get_index(file.0 as usize).map(|p| p.as_path())
    }
}

#[derive(Default)]
pub struct ReadOnlyVfs {
    interner: Interner,
    data: HashMap<FileId, Vec<u8>>,
}

impl ReadOnlyVfs {
    pub fn singleton<P: AsRef<Path>>(path: P, contents: &[u8]) -> Self {
        let mut vfs = ReadOnlyVfs::default();
        vfs.set_file_contents(path, contents);
        vfs
    }
    pub fn alloc_file_id<P: AsRef<Path>>(&mut self, path: P) -> FileId {
        self.interner.intern(path.as_ref().to_owned())
    }
    pub fn len(&self) -> usize {
        self.data.len()
    }
    pub fn file_path(&self, file_id: FileId) -> &Path {
        self.interner.lookup(file_id).unwrap()
    }
    pub fn get(&self, file_id: FileId) -> &Vec<u8> {
        self.data.get(&file_id).unwrap()
    }
    pub fn get_str(&self, file_id: FileId) -> &str {
        std::str::from_utf8(self.get(file_id)).unwrap()
    }
    pub fn get_mut(&mut self, file_id: FileId) -> &mut Vec<u8> {
        self.data.get_mut(&file_id).unwrap()
    }
    pub fn set_file_contents<P: AsRef<Path>>(&mut self, path: P, contents: &[u8]) {
        let file_id = self.alloc_file_id(path);
        self.data.insert(file_id, contents.to_owned());
    }
    pub fn iter(&self) -> impl Iterator<Item = VfsEntry> {
        self.data.iter().map(move |(file_id, _)| VfsEntry {
            file_id: *file_id,
            file_path: self.file_path(*file_id),
            contents: self.get_str(*file_id),
        })
    }
}

pub struct VfsEntry<'ρ> {
    pub file_id: FileId,
    pub file_path: &'ρ Path,
    pub contents: &'ρ str,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn trivial() {
        let mut vfs = ReadOnlyVfs::default();
        let f1 = "a/b/c";
        let id1 = vfs.alloc_file_id(f1);
        let data = "hello".as_bytes().to_vec();
        vfs.set_file_contents(f1, &data);
        assert_eq!(vfs.get(id1), &data);
    }
}