1use ropey::Rope;
9use rustledger_parser::{ParseResult, parse};
10use std::collections::HashMap;
11use std::path::PathBuf;
12use std::sync::Arc;
13
14#[derive(Debug)]
16pub struct Document {
17 content: Rope,
19 version: i32,
21 parse_cache: Option<Arc<ParseResult>>,
23}
24
25impl Document {
26 pub fn new(content: String, version: i32) -> Self {
28 Self {
29 content: Rope::from_str(&content),
30 version,
31 parse_cache: None,
32 }
33 }
34
35 pub fn text(&self) -> String {
37 self.content.to_string()
38 }
39
40 pub fn version(&self) -> i32 {
42 self.version
43 }
44
45 pub fn parse_result(&mut self) -> Arc<ParseResult> {
47 if self.parse_cache.is_none() {
48 let text = self.content.to_string();
49 self.parse_cache = Some(Arc::new(parse(&text)));
50 }
51 self.parse_cache.clone().unwrap()
52 }
53
54 fn invalidate_cache(&mut self) {
56 self.parse_cache = None;
57 }
58
59 pub fn update(&mut self, content: String, version: i32) {
61 self.content = Rope::from_str(&content);
62 self.version = version;
63 self.invalidate_cache();
64 }
65}
66
67#[derive(Debug, Default)]
69pub struct Vfs {
70 documents: HashMap<PathBuf, Document>,
72}
73
74impl Vfs {
75 pub fn new() -> Self {
77 Self::default()
78 }
79
80 pub fn open(&mut self, path: PathBuf, content: String, version: i32) {
82 self.documents.insert(path, Document::new(content, version));
83 }
84
85 pub fn close(&mut self, path: &PathBuf) {
87 self.documents.remove(path);
88 }
89
90 pub fn get(&self, path: &PathBuf) -> Option<&Document> {
92 self.documents.get(path)
93 }
94
95 pub fn get_mut(&mut self, path: &PathBuf) -> Option<&mut Document> {
97 self.documents.get_mut(path)
98 }
99
100 pub fn get_content(&self, path: &PathBuf) -> Option<String> {
102 self.documents.get(path).map(|d| d.text())
103 }
104
105 pub fn get_document_data(&mut self, path: &PathBuf) -> Option<(String, Arc<ParseResult>)> {
108 self.documents.get_mut(path).map(|doc| {
109 let text = doc.text();
110 let parse_result = doc.parse_result();
111 (text, parse_result)
112 })
113 }
114
115 pub fn update(&mut self, path: &PathBuf, content: String, version: i32) {
117 if let Some(doc) = self.documents.get_mut(path) {
118 doc.update(content, version);
119 }
120 }
121
122 pub fn paths(&self) -> impl Iterator<Item = &PathBuf> {
124 self.documents.keys()
125 }
126
127 pub fn iter(&self) -> impl Iterator<Item = (&PathBuf, String)> {
129 self.documents.iter().map(|(path, doc)| (path, doc.text()))
130 }
131
132 pub fn iter_with_parse(
134 &mut self,
135 ) -> impl Iterator<Item = (&PathBuf, String, Arc<ParseResult>)> {
136 self.documents.iter_mut().map(|(path, doc)| {
137 let text = doc.text();
138 let parse_result = doc.parse_result();
139 (path, text, parse_result)
140 })
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_vfs_open_close() {
150 let mut vfs = Vfs::new();
151 let path = PathBuf::from("/test.beancount");
152
153 vfs.open(path.clone(), "2024-01-01 open Assets:Bank".to_string(), 1);
154 assert!(vfs.get(&path).is_some());
155
156 vfs.close(&path);
157 assert!(vfs.get(&path).is_none());
158 }
159
160 #[test]
161 fn test_document_text() {
162 let doc = Document::new("hello world".to_string(), 1);
163 assert_eq!(doc.text(), "hello world");
164 }
165}