Skip to content

Commit 21db163

Browse files
Keavontimon-schelling
authored andcommitted
Avoid duplicate struct definitions in the desktop crate
1 parent d398d80 commit 21db163

3 files changed

Lines changed: 67 additions & 86 deletions

File tree

desktop/src/persist.rs

Lines changed: 64 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,36 @@
1-
use crate::wrapper::messages::{Document, DocumentId};
1+
use crate::wrapper::messages::{Document, DocumentId, PersistedDocumentInfo};
22

3+
// Wraps PersistedState (shared with the web frontend) and adds desktop-specific behavior like file I/O
34
#[derive(Default, serde::Serialize, serde::Deserialize)]
45
pub(crate) struct PersistentData {
5-
documents: DocumentStore,
6+
documents: Vec<PersistedDocumentInfo>,
67
current_document: Option<DocumentId>,
78
#[serde(skip)]
89
document_order: Option<Vec<DocumentId>>,
910
}
1011

1112
impl PersistentData {
1213
pub(crate) fn write_document(&mut self, id: DocumentId, document: Document) {
13-
self.documents.write(id, document);
14+
// Update or add the document metadata
15+
let info = PersistedDocumentInfo {
16+
id,
17+
name: document.name.clone(),
18+
path: document.path.clone(),
19+
is_saved: document.is_saved,
20+
};
21+
if let Some(existing) = self.documents.iter_mut().find(|doc| doc.id == id) {
22+
*existing = info;
23+
} else {
24+
self.documents.push(info);
25+
}
26+
27+
// Write the document content to a separate file
28+
if let Err(e) = std::fs::write(Self::document_content_path(&id), document.content) {
29+
tracing::error!("Failed to write document {id:?} to disk: {e}");
30+
}
31+
1432
if let Some(order) = &self.document_order {
15-
self.documents.force_order(order);
33+
self.force_order(order.clone());
1634
}
1735
self.flush();
1836
}
@@ -21,31 +39,35 @@ impl PersistentData {
2139
if Some(*id) == self.current_document {
2240
self.current_document = None;
2341
}
24-
self.documents.delete(id);
42+
43+
self.documents.retain(|doc| doc.id != *id);
44+
if let Err(e) = std::fs::remove_file(Self::document_content_path(id)) {
45+
tracing::error!("Failed to delete document {id:?} from disk: {e}");
46+
}
47+
2548
self.flush();
2649
}
2750

2851
pub(crate) fn current_document_id(&self) -> Option<DocumentId> {
2952
match self.current_document {
3053
Some(id) => Some(id),
31-
None => Some(*self.documents.document_ids().first()?),
54+
None => Some(self.documents.first()?.id),
3255
}
3356
}
3457

3558
pub(crate) fn current_document(&self) -> Option<(DocumentId, Document)> {
3659
let current_id = self.current_document_id()?;
37-
Some((current_id, self.documents.read(&current_id)?))
60+
Some((current_id, self.read_document(&current_id)?))
3861
}
3962

4063
pub(crate) fn documents_before_current(&self) -> Vec<(DocumentId, Document)> {
4164
let Some(current_id) = self.current_document_id() else {
4265
return Vec::new();
4366
};
4467
self.documents
45-
.document_ids()
46-
.into_iter()
47-
.take_while(|id| *id != current_id)
48-
.filter_map(|id| Some((id, self.documents.read(&id)?)))
68+
.iter()
69+
.take_while(|doc| doc.id != current_id)
70+
.filter_map(|doc| Some((doc.id, self.read_document(&doc.id)?)))
4971
.collect()
5072
}
5173

@@ -54,11 +76,10 @@ impl PersistentData {
5476
return Vec::new();
5577
};
5678
self.documents
57-
.document_ids()
58-
.into_iter()
59-
.skip_while(|id| *id != current_id)
79+
.iter()
80+
.skip_while(|doc| doc.id != current_id)
6081
.skip(1)
61-
.filter_map(|id| Some((id, self.documents.read(&id)?)))
82+
.filter_map(|doc| Some((doc.id, self.read_document(&doc.id)?)))
6283
.collect()
6384
}
6485

@@ -68,11 +89,37 @@ impl PersistentData {
6889
}
6990

7091
pub(crate) fn force_document_order(&mut self, order: Vec<DocumentId>) {
92+
self.force_order(order.clone());
7193
self.document_order = Some(order);
72-
self.documents.force_order(self.document_order.as_ref().unwrap());
7394
self.flush();
7495
}
7596

97+
// Reads serialized document content from disk and combines it with the stored metadata
98+
fn read_document(&self, id: &DocumentId) -> Option<Document> {
99+
let info = self.documents.iter().find(|doc| doc.id == *id)?;
100+
let content = std::fs::read_to_string(Self::document_content_path(id)).ok()?;
101+
Some(Document {
102+
content,
103+
name: info.name.clone(),
104+
path: info.path.clone(),
105+
is_saved: info.is_saved,
106+
})
107+
}
108+
109+
// Reorders the documents array to match a desired ordering, keeping unmentioned documents at the end
110+
fn force_order(&mut self, desired_order: Vec<DocumentId>) {
111+
let mut ordered_prefix_length = 0;
112+
for id in &desired_order {
113+
if let Some(offset) = self.documents[ordered_prefix_length..].iter().position(|doc| doc.id == *id) {
114+
let found_index = ordered_prefix_length + offset;
115+
if found_index != ordered_prefix_length {
116+
self.documents[ordered_prefix_length..=found_index].rotate_right(1);
117+
}
118+
ordered_prefix_length += 1;
119+
}
120+
}
121+
}
122+
76123
fn flush(&self) {
77124
let data = match ron::ser::to_string_pretty(self, Default::default()) {
78125
Ok(d) => d,
@@ -114,79 +161,10 @@ impl PersistentData {
114161
path.push(crate::consts::APP_STATE_FILE_NAME);
115162
path
116163
}
117-
}
118-
119-
#[derive(Default, serde::Serialize, serde::Deserialize)]
120-
struct DocumentStore(Vec<DocumentInfo>);
121-
impl DocumentStore {
122-
fn write(&mut self, id: DocumentId, document: Document) {
123-
let meta = DocumentInfo::new(id, &document);
124-
if let Some(existing) = self.0.iter_mut().find(|meta| meta.id == id) {
125-
*existing = meta;
126-
} else {
127-
self.0.push(meta);
128-
}
129-
if let Err(e) = std::fs::write(Self::document_path(&id), document.content) {
130-
tracing::error!("Failed to write document {id:?} to disk: {e}");
131-
}
132-
}
133-
134-
fn delete(&mut self, id: &DocumentId) {
135-
self.0.retain(|meta| meta.id != *id);
136-
if let Err(e) = std::fs::remove_file(Self::document_path(id)) {
137-
tracing::error!("Failed to delete document {id:?} from disk: {e}");
138-
}
139-
}
140-
141-
fn read(&self, id: &DocumentId) -> Option<Document> {
142-
let meta = self.0.iter().find(|meta| meta.id == *id)?;
143-
let content = std::fs::read_to_string(Self::document_path(id)).ok()?;
144-
Some(Document {
145-
content,
146-
name: meta.name.clone(),
147-
path: meta.path.clone(),
148-
is_saved: meta.is_saved,
149-
})
150-
}
151164

152-
fn force_order(&mut self, desired_order: &[DocumentId]) {
153-
let mut ordered_prefix_len = 0;
154-
for id in desired_order {
155-
if let Some(offset) = self.0[ordered_prefix_len..].iter().position(|meta| meta.id == *id) {
156-
let found_index = ordered_prefix_len + offset;
157-
if found_index != ordered_prefix_len {
158-
self.0[ordered_prefix_len..=found_index].rotate_right(1);
159-
}
160-
ordered_prefix_len += 1;
161-
}
162-
}
163-
}
164-
165-
fn document_ids(&self) -> Vec<DocumentId> {
166-
self.0.iter().map(|meta| meta.id).collect()
167-
}
168-
169-
fn document_path(id: &DocumentId) -> std::path::PathBuf {
165+
fn document_content_path(id: &DocumentId) -> std::path::PathBuf {
170166
let mut path = crate::dirs::app_autosave_documents_dir();
171167
path.push(format!("{:x}.{}", id.0, graphite_desktop_wrapper::FILE_EXTENSION));
172168
path
173169
}
174170
}
175-
176-
#[derive(serde::Serialize, serde::Deserialize)]
177-
struct DocumentInfo {
178-
id: DocumentId,
179-
name: String,
180-
path: Option<std::path::PathBuf>,
181-
is_saved: bool,
182-
}
183-
impl DocumentInfo {
184-
fn new(id: DocumentId, Document { name, path, is_saved, .. }: &Document) -> Self {
185-
Self {
186-
id,
187-
name: name.clone(),
188-
path: path.clone(),
189-
is_saved: *is_saved,
190-
}
191-
}
192-
}

desktop/wrapper/src/messages.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::path::PathBuf;
33

44
pub(crate) use graphite_editor::messages::prelude::Message as EditorMessage;
55

6+
pub use graphite_editor::messages::frontend::utility_types::{PersistedDocumentInfo, PersistedState};
67
pub use graphite_editor::messages::input_mapper::utility_types::input_keyboard::{Key, ModifierKeys};
78
pub use graphite_editor::messages::input_mapper::utility_types::input_mouse::{EditorMouseState as MouseState, EditorPosition as Position, MouseKeys};
89
pub use graphite_editor::messages::prelude::DocumentId;

editor/src/messages/frontend/utility_types.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ pub struct DocumentDetails {
2626
pub struct PersistedDocumentInfo {
2727
pub id: DocumentId,
2828
pub name: String,
29+
#[serde(default)]
30+
pub path: Option<PathBuf>,
2931
pub is_saved: bool,
3032
}
3133

0 commit comments

Comments
 (0)