|
1 | | -use crate::wrapper::messages::{Document, DocumentId, PersistedDocumentInfo}; |
2 | | - |
3 | | -#[derive(Default, serde::Serialize, serde::Deserialize)] |
4 | | -pub(crate) struct PersistentData { |
5 | | - documents: Vec<PersistedDocumentInfo>, |
6 | | - current_document: Option<DocumentId>, |
7 | | - #[serde(skip)] |
8 | | - document_order: Option<Vec<DocumentId>>, |
9 | | -} |
10 | | - |
11 | | -impl PersistentData { |
12 | | - pub(crate) fn write_document(&mut self, id: DocumentId, document: Document) { |
13 | | - let info = PersistedDocumentInfo { |
14 | | - id, |
15 | | - name: document.name.clone(), |
16 | | - path: document.path.clone(), |
17 | | - is_saved: document.is_saved, |
18 | | - }; |
19 | | - if let Some(existing) = self.documents.iter_mut().find(|doc| doc.id == id) { |
20 | | - *existing = info; |
21 | | - } else { |
22 | | - self.documents.push(info); |
| 1 | +use crate::wrapper::messages::{DocumentId, PersistedState}; |
| 2 | + |
| 3 | +pub(crate) fn read_state() -> PersistedState { |
| 4 | + let path = state_file_path(); |
| 5 | + let data = match std::fs::read_to_string(&path) { |
| 6 | + Ok(d) => d, |
| 7 | + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { |
| 8 | + tracing::info!("No persistent data file found at {path:?}, starting fresh"); |
| 9 | + return PersistedState::default(); |
23 | 10 | } |
24 | | - |
25 | | - if let Err(e) = std::fs::write(Self::document_content_path(&id), document.content) { |
26 | | - tracing::error!("Failed to write document {id:?} to disk: {e}"); |
| 11 | + Err(e) => { |
| 12 | + tracing::error!("Failed to read persistent data from disk: {e}"); |
| 13 | + return PersistedState::default(); |
27 | 14 | } |
28 | | - |
29 | | - self.flush(); |
30 | | - } |
31 | | - |
32 | | - pub(crate) fn delete_document(&mut self, id: &DocumentId) { |
33 | | - if Some(*id) == self.current_document { |
34 | | - self.current_document = None; |
35 | | - } |
36 | | - |
37 | | - self.documents.retain(|doc| doc.id != *id); |
38 | | - if let Err(e) = std::fs::remove_file(Self::document_content_path(id)) { |
39 | | - tracing::error!("Failed to delete document {id:?} from disk: {e}"); |
| 15 | + }; |
| 16 | + let loaded = match ron::from_str(&data) { |
| 17 | + Ok(d) => d, |
| 18 | + Err(e) => { |
| 19 | + tracing::error!("Failed to deserialize persistent data: {e}"); |
| 20 | + return PersistedState::default(); |
40 | 21 | } |
| 22 | + }; |
41 | 23 |
|
42 | | - self.flush(); |
43 | | - } |
| 24 | + garbage_collect_document_files(&loaded); |
| 25 | + loaded |
| 26 | +} |
44 | 27 |
|
45 | | - pub(crate) fn current_document_id(&self) -> Option<DocumentId> { |
46 | | - match self.current_document { |
47 | | - Some(id) => Some(id), |
48 | | - None => Some(self.documents.first()?.id), |
| 28 | +pub(crate) fn write_state(state: PersistedState) { |
| 29 | + let state: &PersistedState = &state; |
| 30 | + let data = match ron::ser::to_string_pretty(state, Default::default()) { |
| 31 | + Ok(d) => d, |
| 32 | + Err(e) => { |
| 33 | + tracing::error!("Failed to serialize persistent data: {e}"); |
| 34 | + return; |
49 | 35 | } |
| 36 | + }; |
| 37 | + if let Err(e) = std::fs::write(state_file_path(), data) { |
| 38 | + tracing::error!("Failed to write persistent data to disk: {e}"); |
50 | 39 | } |
| 40 | + garbage_collect_document_files(&state); |
| 41 | +} |
51 | 42 |
|
52 | | - pub(crate) fn documents(&self) -> Vec<(DocumentId, Document)> { |
53 | | - self.documents.iter().filter_map(|doc| Some((doc.id, self.read_document(&doc.id)?))).collect() |
| 43 | +pub(crate) fn write_document_content(id: DocumentId, document_content: String) { |
| 44 | + if let Err(e) = std::fs::write(document_content_path(&id), document_content) { |
| 45 | + tracing::error!("Failed to write document {id:?} to disk: {e}"); |
54 | 46 | } |
| 47 | +} |
55 | 48 |
|
56 | | - pub(crate) fn set_current_document(&mut self, id: DocumentId) { |
57 | | - self.current_document = Some(id); |
58 | | - self.flush(); |
59 | | - } |
| 49 | +pub(crate) fn read_document_content(id: &DocumentId) -> Option<String> { |
| 50 | + std::fs::read_to_string(document_content_path(id)).ok() |
| 51 | +} |
60 | 52 |
|
61 | | - pub(crate) fn force_document_order(&mut self, order: Vec<DocumentId>) { |
62 | | - let mut ordered_prefix_length = 0; |
63 | | - for id in &order { |
64 | | - if let Some(offset) = self.documents[ordered_prefix_length..].iter().position(|doc| doc.id == *id) { |
65 | | - let found_index = ordered_prefix_length + offset; |
66 | | - if found_index != ordered_prefix_length { |
67 | | - self.documents[ordered_prefix_length..=found_index].rotate_right(1); |
68 | | - } |
69 | | - ordered_prefix_length += 1; |
70 | | - } |
71 | | - } |
72 | | - self.document_order = Some(order); |
73 | | - self.flush(); |
| 53 | +pub(crate) fn delete_document(id: &DocumentId) { |
| 54 | + if let Err(e) = std::fs::remove_file(document_content_path(id)) { |
| 55 | + tracing::error!("Failed to delete document {id:?} from disk: {e}"); |
74 | 56 | } |
| 57 | +} |
75 | 58 |
|
76 | | - fn read_document(&self, id: &DocumentId) -> Option<Document> { |
77 | | - let info = self.documents.iter().find(|doc| doc.id == *id)?; |
78 | | - let content = std::fs::read_to_string(Self::document_content_path(id)).ok()?; |
79 | | - Some(Document { |
80 | | - content, |
81 | | - name: info.name.clone(), |
82 | | - path: info.path.clone(), |
83 | | - is_saved: info.is_saved, |
84 | | - }) |
85 | | - } |
| 59 | +fn garbage_collect_document_files(state: &PersistedState) { |
| 60 | + let valid_paths: std::collections::HashSet<_> = state.documents.iter().map(|doc| document_content_path(&doc.id)).collect(); |
86 | 61 |
|
87 | | - fn flush(&self) { |
88 | | - let data = match ron::ser::to_string_pretty(self, Default::default()) { |
89 | | - Ok(d) => d, |
90 | | - Err(e) => { |
91 | | - tracing::error!("Failed to serialize persistent data: {e}"); |
92 | | - return; |
93 | | - } |
94 | | - }; |
95 | | - if let Err(e) = std::fs::write(Self::state_file_path(), data) { |
96 | | - tracing::error!("Failed to write persistent data to disk: {e}"); |
| 62 | + let directory = crate::dirs::app_autosave_documents_dir(); |
| 63 | + let entries = match std::fs::read_dir(&directory) { |
| 64 | + Ok(entries) => entries, |
| 65 | + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return, |
| 66 | + Err(e) => { |
| 67 | + tracing::error!("Failed to read autosave documents directory: {e}"); |
| 68 | + return; |
97 | 69 | } |
98 | | - } |
99 | | - |
100 | | - pub(crate) fn load_from_disk(&mut self) { |
101 | | - delete_old_cef_browser_directory(); |
102 | | - |
103 | | - let path = Self::state_file_path(); |
104 | | - let data = match std::fs::read_to_string(&path) { |
105 | | - Ok(d) => d, |
106 | | - Err(e) if e.kind() == std::io::ErrorKind::NotFound => { |
107 | | - tracing::info!("No persistent data file found at {path:?}, starting fresh"); |
108 | | - return; |
109 | | - } |
110 | | - Err(e) => { |
111 | | - tracing::error!("Failed to read persistent data from disk: {e}"); |
112 | | - return; |
113 | | - } |
114 | | - }; |
115 | | - let loaded = match ron::from_str(&data) { |
116 | | - Ok(d) => d, |
117 | | - Err(e) => { |
118 | | - tracing::error!("Failed to deserialize persistent data: {e}"); |
119 | | - return; |
120 | | - } |
121 | | - }; |
122 | | - *self = loaded; |
123 | | - |
124 | | - self.garbage_collect_document_files(); |
125 | | - } |
126 | | - |
127 | | - fn garbage_collect_document_files(&self) { |
128 | | - let valid_paths: std::collections::HashSet<_> = self.documents.iter().map(|doc| Self::document_content_path(&doc.id)).collect(); |
| 70 | + }; |
129 | 71 |
|
130 | | - let directory = crate::dirs::app_autosave_documents_dir(); |
131 | | - let entries = match std::fs::read_dir(&directory) { |
132 | | - Ok(entries) => entries, |
133 | | - Err(e) if e.kind() == std::io::ErrorKind::NotFound => return, |
134 | | - Err(e) => { |
135 | | - tracing::error!("Failed to read autosave documents directory: {e}"); |
136 | | - return; |
137 | | - } |
138 | | - }; |
139 | | - |
140 | | - for entry in entries.flatten() { |
141 | | - let path = entry.path(); |
142 | | - if path.is_file() && !valid_paths.contains(&path) { |
143 | | - if let Err(e) = std::fs::remove_file(&path) { |
144 | | - tracing::error!("Failed to remove orphaned document file {path:?}: {e}"); |
145 | | - } |
| 72 | + for entry in entries.flatten() { |
| 73 | + let path = entry.path(); |
| 74 | + if path.is_file() && !valid_paths.contains(&path) { |
| 75 | + if let Err(e) = std::fs::remove_file(&path) { |
| 76 | + tracing::error!("Failed to remove orphaned document file {path:?}: {e}"); |
146 | 77 | } |
147 | 78 | } |
148 | 79 | } |
| 80 | +} |
149 | 81 |
|
150 | | - fn state_file_path() -> std::path::PathBuf { |
151 | | - let mut path = crate::dirs::app_data_dir(); |
152 | | - path.push(crate::consts::APP_STATE_FILE_NAME); |
153 | | - path |
154 | | - } |
155 | | - |
156 | | - fn document_content_path(id: &DocumentId) -> std::path::PathBuf { |
157 | | - let mut path = crate::dirs::app_autosave_documents_dir(); |
158 | | - path.push(format!("{:x}.{}", id.0, graphite_desktop_wrapper::FILE_EXTENSION)); |
159 | | - path |
160 | | - } |
| 82 | +fn state_file_path() -> std::path::PathBuf { |
| 83 | + let mut path = crate::dirs::app_data_dir(); |
| 84 | + path.push(crate::consts::APP_STATE_FILE_NAME); |
| 85 | + path |
161 | 86 | } |
162 | 87 |
|
163 | | -// TODO: Eventually remove this cleanup code for the old "browser" CEF directory |
164 | | -fn delete_old_cef_browser_directory() { |
165 | | - let old_browser_dir = crate::dirs::app_data_dir().join("browser"); |
166 | | - if old_browser_dir.is_dir() { |
167 | | - let _ = std::fs::remove_dir_all(&old_browser_dir); |
168 | | - } |
| 88 | +fn document_content_path(id: &DocumentId) -> std::path::PathBuf { |
| 89 | + let mut path = crate::dirs::app_autosave_documents_dir(); |
| 90 | + path.push(format!("{:x}.{}", id.0, graphite_desktop_wrapper::FILE_EXTENSION)); |
| 91 | + path |
169 | 92 | } |
0 commit comments