use indexmap::IndexMap;
use parking_lot::RwLock;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use crate::base::FileId;
#[derive(Debug, Default)]
pub struct FileSet {
inner: RwLock<FileSetInner>,
}
#[derive(Debug, Default)]
struct FileSetInner {
path_to_id: IndexMap<PathBuf, FileId>,
id_to_path: IndexMap<FileId, PathBuf>,
contents: IndexMap<FileId, Arc<str>>,
next_id: u32,
}
impl FileSet {
pub fn new() -> Self {
Self::default()
}
pub fn file_id(&self, path: &Path) -> FileId {
{
let inner = self.inner.read();
if let Some(&id) = inner.path_to_id.get(path) {
return id;
}
}
let mut inner = self.inner.write();
if let Some(&id) = inner.path_to_id.get(path) {
return id;
}
let id = FileId::new(inner.next_id);
inner.next_id += 1;
inner.path_to_id.insert(path.to_owned(), id);
inner.id_to_path.insert(id, path.to_owned());
id
}
pub fn path(&self, file: FileId) -> Option<PathBuf> {
self.inner.read().id_to_path.get(&file).cloned()
}
pub fn set_contents(&self, file: FileId, contents: impl Into<Arc<str>>) {
self.inner.write().contents.insert(file, contents.into());
}
pub fn contents(&self, file: FileId) -> Option<Arc<str>> {
self.inner.read().contents.get(&file).cloned()
}
pub fn remove(&self, file: FileId) {
let mut inner = self.inner.write();
if let Some(path) = inner.id_to_path.swap_remove(&file) {
inner.path_to_id.swap_remove(&path);
}
inner.contents.swap_remove(&file);
}
pub fn len(&self) -> usize {
self.inner.read().path_to_id.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn files(&self) -> Vec<FileId> {
self.inner.read().id_to_path.keys().copied().collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_file_set_id_assignment() {
let files = FileSet::new();
let id1 = files.file_id(Path::new("/a.sysml"));
let id2 = files.file_id(Path::new("/b.sysml"));
let id3 = files.file_id(Path::new("/a.sysml"));
assert_ne!(id1, id2);
assert_eq!(id1, id3); }
#[test]
fn test_file_set_contents() {
let files = FileSet::new();
let id = files.file_id(Path::new("/test.sysml"));
assert!(files.contents(id).is_none());
files.set_contents(id, "part def Foo;");
assert_eq!(files.contents(id).as_deref(), Some("part def Foo;"));
}
#[test]
fn test_file_set_path_lookup() {
let files = FileSet::new();
let path = Path::new("/test.sysml");
let id = files.file_id(path);
assert_eq!(files.path(id).as_deref(), Some(path));
}
}