use ropey::Rope;
use rustledger_parser::{ParseResult, parse};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
#[derive(Debug)]
pub struct Document {
content: Rope,
version: i32,
parse_cache: Option<Arc<ParseResult>>,
}
impl Document {
pub fn new(content: String, version: i32) -> Self {
Self {
content: Rope::from_str(&content),
version,
parse_cache: None,
}
}
pub fn text(&self) -> String {
self.content.to_string()
}
pub fn version(&self) -> i32 {
self.version
}
pub fn parse_result(&mut self) -> Arc<ParseResult> {
if self.parse_cache.is_none() {
let text = self.content.to_string();
self.parse_cache = Some(Arc::new(parse(&text)));
}
self.parse_cache.clone().unwrap()
}
fn invalidate_cache(&mut self) {
self.parse_cache = None;
}
pub fn update(&mut self, content: String, version: i32) {
self.content = Rope::from_str(&content);
self.version = version;
self.invalidate_cache();
}
}
#[derive(Debug, Default)]
pub struct Vfs {
documents: HashMap<PathBuf, Document>,
}
impl Vfs {
pub fn new() -> Self {
Self::default()
}
pub fn open(&mut self, path: PathBuf, content: String, version: i32) {
self.documents.insert(path, Document::new(content, version));
}
pub fn close(&mut self, path: &PathBuf) {
self.documents.remove(path);
}
pub fn get(&self, path: &PathBuf) -> Option<&Document> {
self.documents.get(path)
}
pub fn get_mut(&mut self, path: &PathBuf) -> Option<&mut Document> {
self.documents.get_mut(path)
}
pub fn get_content(&self, path: &PathBuf) -> Option<String> {
self.documents.get(path).map(|d| d.text())
}
pub fn get_document_data(&mut self, path: &PathBuf) -> Option<(String, Arc<ParseResult>)> {
self.documents.get_mut(path).map(|doc| {
let text = doc.text();
let parse_result = doc.parse_result();
(text, parse_result)
})
}
pub fn update(&mut self, path: &PathBuf, content: String, version: i32) {
if let Some(doc) = self.documents.get_mut(path) {
doc.update(content, version);
}
}
pub fn paths(&self) -> impl Iterator<Item = &PathBuf> {
self.documents.keys()
}
pub fn iter(&self) -> impl Iterator<Item = (&PathBuf, String)> {
self.documents.iter().map(|(path, doc)| (path, doc.text()))
}
pub fn iter_with_parse(
&mut self,
) -> impl Iterator<Item = (&PathBuf, String, Arc<ParseResult>)> {
self.documents.iter_mut().map(|(path, doc)| {
let text = doc.text();
let parse_result = doc.parse_result();
(path, text, parse_result)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vfs_open_close() {
let mut vfs = Vfs::new();
let path = PathBuf::from("/test.beancount");
vfs.open(path.clone(), "2024-01-01 open Assets:Bank".to_string(), 1);
assert!(vfs.get(&path).is_some());
vfs.close(&path);
assert!(vfs.get(&path).is_none());
}
#[test]
fn test_document_text() {
let doc = Document::new("hello world".to_string(), 1);
assert_eq!(doc.text(), "hello world");
}
}