Better save file

This commit is contained in:
elvis
2025-10-30 02:02:10 +01:00
parent cce3b5a7a0
commit dd596491c7

View File

@ -207,8 +207,8 @@ impl Hash for BasicValue {
Set, Set,
Context, Context,
Reactions, Reactions,
PositiveSystem,
Trace, Trace,
PositiveSystem,
PositiveTrace, PositiveTrace,
PositiveSet, PositiveSet,
PositiveEnvironment, PositiveEnvironment,
@ -888,8 +888,8 @@ pub struct GlobalState {
pub active_node: Option<NodeId>, pub active_node: Option<NodeId>,
pub save_node: Option<NodeId>, pub save_node: Option<NodeId>,
pub display_result: bool, pub display_result: bool,
pub translator: rsprocess::translator::Translator,
pub translator: rsprocess::translator::Translator,
pub cache: OutputsCache, pub cache: OutputsCache,
} }
@ -1129,10 +1129,9 @@ impl NodeTemplateTrait for NodeInstruction {
| Self::PositiveGraph | Self::PositiveGraph
| Self::PositiveDot | Self::PositiveDot
| Self::PositiveGraphML | Self::PositiveGraphML
| Self::PositiveAssertFunction => | Self::PositiveAssertFunction
vec!["Positive System", "Positive Graph"], | Self::PositiveGroupFunction
| Self::PositiveGroupFunction | Self::PositiveGroupNodes => | Self::PositiveGroupNodes => vec!["Positive Graph"],
vec!["Positive Graph"],
| Self::Trace => vec!["Trace", "System"], | Self::Trace => vec!["Trace", "System"],
| Self::PositiveTrace => vec!["Trace", "Positive System"], | Self::PositiveTrace => vec!["Trace", "Positive System"],
| Self::SliceTrace | Self::SliceTrace
@ -1508,6 +1507,15 @@ pub struct AppHandle {
#[cfg(feature = "persistence")] #[cfg(feature = "persistence")]
const PERSISTENCE_KEY: &str = "egui_node_graph"; const PERSISTENCE_KEY: &str = "egui_node_graph";
#[cfg(feature = "persistence")]
const TRANSLATOR_KEY: &str = "egui_node_graph_translator";
#[cfg(feature = "persistence")]
const CACHE_KEY: &str = "egui_node_graph_cache";
#[cfg(feature = "persistence")]
const VERSION_NUMBER: u64 = 1;
#[cfg(feature = "persistence")] #[cfg(feature = "persistence")]
impl AppHandle { impl AppHandle {
/// If the persistence feature is enabled, Called once before the first /// If the persistence feature is enabled, Called once before the first
@ -1517,13 +1525,110 @@ impl AppHandle {
.storage .storage
.and_then(|storage| eframe::get_value(storage, PERSISTENCE_KEY)) .and_then(|storage| eframe::get_value(storage, PERSISTENCE_KEY))
.unwrap_or_default(); .unwrap_or_default();
Self { let cache = cc
state, .storage
user_state: GlobalState::default(), .and_then(|storage| eframe::get_value(storage, CACHE_KEY))
} .unwrap_or_default();
let translator = cc
.storage
.and_then(|storage| eframe::get_value(storage, TRANSLATOR_KEY))
.unwrap_or_default();
let user_state = GlobalState {
cache,
translator,
..Default::default()
};
Self { state, user_state, }
} }
} }
#[cfg(feature = "persistence")]
fn write_state(
state: &str,
translator: &str,
cache: &str,
path: &std::path::PathBuf
) -> std::io::Result<()> {
use std::io::{Write, BufWriter};
use std::fs::File;
let f = File::create(path)?;
let mut writer = BufWriter::new(f);
writer.write_all(&VERSION_NUMBER.to_le_bytes())?;
writer.write_all(&(state.len() as u64).to_le_bytes())?;
writer.write_all(&(translator.len() as u64).to_le_bytes())?;
writer.write_all(&(cache.len() as u64).to_le_bytes())?;
writer.write_all(state.as_bytes())?;
writer.write_all(translator.as_bytes())?;
writer.write_all(cache.as_bytes())?;
Ok(())
}
#[cfg(feature = "persistence")]
fn read_state(
path: &std::path::PathBuf
) -> Result<(EditorState, rsprocess::translator::Translator, OutputsCache), String> {
use std::io::Read;
use std::fs::File;
use rsprocess::translator::Translator;
let mut f = File::open(path).map_err(|e| format!("{e}"))?;
let version = {
let mut buffer = [0; 8];
f.read_exact(&mut buffer).map_err(|e| format!("{e}"))?;
u64::from_le_bytes(buffer)
};
if version != VERSION_NUMBER {
println!("WARNING: Reading file but version mismatch");
}
let len_state = {
let mut buffer = [0; 8];
f.read_exact(&mut buffer).map_err(|e| format!("{e}"))?;
u64::from_le_bytes(buffer)
};
let len_translator = {
let mut buffer = [0; 8];
f.read_exact(&mut buffer).map_err(|e| format!("{e}"))?;
u64::from_le_bytes(buffer)
};
let len_cache = {
let mut buffer = [0; 8];
f.read_exact(&mut buffer).map_err(|e| format!("{e}"))?;
u64::from_le_bytes(buffer)
};
let string_state = {
let mut s = Vec::new();
s.reserve_exact(len_state as usize);
f.by_ref().take(len_state).read_to_end(&mut s).map_err(|e| format!("{e}"))?;
String::from_utf8(s).map_err(|e| format!("{e}"))?
};
let string_translator = {
let mut s = Vec::new();
s.reserve_exact(len_translator as usize);
f.by_ref().take(len_translator).read_to_end(&mut s).map_err(|e| format!("{e}"))?;
String::from_utf8(s).map_err(|e| format!("{e}"))?
};
let string_cache = {
let mut s = Vec::new();
s.reserve_exact(len_cache as usize);
f.by_ref().take(len_cache).read_to_end(&mut s).map_err(|e| format!("{e}"))?;
String::from_utf8(s).map_err(|e| format!("{e}"))?
};
let state = ron::from_str::<EditorState>(&string_state).map_err(|e| format!("{e}"))?;
let translator = ron::from_str::<Translator>(&string_translator).map_err(|e| format!("{e}"))?;
let cache = ron::from_str::<OutputsCache>(&string_cache).map_err(|e| format!("{e}"))?;
Ok((state, translator, cache))
}
/// Main endpoint to be executed /// Main endpoint to be executed
impl eframe::App for AppHandle { impl eframe::App for AppHandle {
#[cfg(feature = "persistence")] #[cfg(feature = "persistence")]
@ -1531,6 +1636,8 @@ impl eframe::App for AppHandle {
/// Called by the frame work to save state before shutdown. /// Called by the frame work to save state before shutdown.
fn save(&mut self, storage: &mut dyn eframe::Storage) { fn save(&mut self, storage: &mut dyn eframe::Storage) {
eframe::set_value(storage, PERSISTENCE_KEY, &self.state); eframe::set_value(storage, PERSISTENCE_KEY, &self.state);
eframe::set_value(storage, TRANSLATOR_KEY, &self.user_state.translator);
eframe::set_value(storage, CACHE_KEY, &self.user_state.cache);
} }
/// Called each time the UI needs repainting, which may be many times per /// Called each time the UI needs repainting, which may be many times per
@ -1571,6 +1678,7 @@ impl eframe::App for AppHandle {
&new_state, &new_state,
); );
self.state = new_state; self.state = new_state;
self.user_state.cache = Default::default();
ui.close(); ui.close();
} }
if ui.button("Open File…").clicked() if ui.button("Open File…").clicked()
@ -1578,22 +1686,33 @@ impl eframe::App for AppHandle {
.add_filter("ron", &["ron"]) .add_filter("ron", &["ron"])
.pick_file() .pick_file()
{ {
if let Ok(s) = std::fs::read_to_string(path) { match read_state(&path) {
match ron::from_str::<EditorState>(&s) { Ok((state, translator, cache)) => {
| Ok(state) => { eframe::set_value(
eframe::set_value( _frame
_frame .storage_mut()
.storage_mut() .expect("no storage found"),
.expect("no storage found"), PERSISTENCE_KEY,
PERSISTENCE_KEY, &state,
&state, );
); self.state = state;
self.state = state;
}, eframe::set_value(
| Err(err) => { _frame
println!("error {err:?}"); .storage_mut()
}, .expect("no storage found"),
} TRANSLATOR_KEY, &translator,
);
self.user_state.translator = translator;
eframe::set_value(
_frame
.storage_mut()
.expect("no storage found"),
CACHE_KEY, &cache);
self.user_state.cache = cache;
},
Err(e) => println!("Error reading file: {e}"),
} }
ui.close(); ui.close();
} }
@ -1602,19 +1721,33 @@ impl eframe::App for AppHandle {
.add_filter("ron", &["ron"]) .add_filter("ron", &["ron"])
.save_file() .save_file()
{ {
let value = let state =
match ron::ser::to_string(&self.state) { match ron::ser::to_string(&self.state) {
| Ok(value) => value, | Ok(value) => value,
| Err(e) => { | Err(e) => {
println!("error {e}"); println!("Error serializing: {e}");
panic!() panic!()
}, },
}; };
match std::fs::write(path, value) { let translator =
| Ok(_) => {}, match ron::ser::to_string(&self.user_state.translator) {
| Err(e) => { | Ok(value) => value,
println!("error saving {e:?}") | Err(e) => {
}, println!("Error serializing: {e}");
panic!()
},
};
let cache =
match ron::ser::to_string(&self.user_state.cache) {
| Ok(value) => value,
| Err(e) => {
println!("Error serializing: {e}");
panic!()
},
};
match write_state(&state, &translator, &cache, &path) {
Ok(_) => {},
Err(e) => println!("Could not save file: {e}"),
} }
ui.close(); ui.close();
@ -1751,20 +1884,12 @@ fn create_output(ng: &mut AppHandle, ctx: &egui::Context) -> LayoutJob {
| Ok(BasicValue::SaveString { path, value }) => { | Ok(BasicValue::SaveString { path, value }) => {
match std::fs::write(&path, value) { match std::fs::write(&path, value) {
| Ok(_) => { | Ok(_) => {
// TODO: this only appears for one frame
text.append( text.append(
&format!("Wrote file {}.", path), &format!("Wrote file {}.", path),
0., 0., Default::default());
TextFormat {
..Default::default()
},
);
}, },
| Err(e) => { | Err(e) => {
// TODO: this only appears for one frame text.append(&format!("{e}"), 0., Default::default());
text.append(&format!("{e}"), 0., TextFormat {
..Default::default()
});
}, },
} }
}, },