From d0ced90bb1bae561b867a6ebf182fa811ee3d75c Mon Sep 17 00:00:00 2001 From: hinto-janaiyo Date: Sun, 20 Nov 2022 13:31:00 -0500 Subject: [PATCH] main/disk: get OS data path only once, pass ref for [state/node] --- src/disk.rs | 70 ++++++++------------ src/gupax.rs | 12 ++-- src/main.rs | 176 +++++++++++++++++++++++++++++++-------------------- 3 files changed, 140 insertions(+), 118 deletions(-) diff --git a/src/disk.rs b/src/disk.rs index 28ef127..b266304 100644 --- a/src/disk.rs +++ b/src/disk.rs @@ -50,39 +50,25 @@ use log::*; // create_new() | Write a default TOML Struct into the appropriate file (in OS data path) // into_absolute_path() | Convert relative -> absolute path -pub fn get_os_data_path() -> Result { +pub fn get_gupax_data_path() -> Result { // Get OS data folder - // Linux | $XDG_DATA_HOME or $HOME/.local/share | /home/alice/.local/state - // macOS | $HOME/Library/Application Support | /Users/Alice/Library/Application Support - // Windows | {FOLDERID_RoamingAppData} | C:\Users\Alice\AppData\Roaming - let path = match dirs::data_dir() { - Some(path) => { - info!("OS | Data path ... OK"); - path - }, - None => { error!("OS | Data path ... FAIL"); return Err(TomlError::Path(PATH_ERROR.to_string())) }, - }; - // Create directory - fs::create_dir_all(&path)?; - Ok(path) -} - -pub fn get_file_path(file: File) -> Result { - let name = File::name(&file); - - let mut path = match dirs::data_dir() { + // Linux | $XDG_DATA_HOME or $HOME/.local/share/gupax | /home/alice/.local/state/gupax + // macOS | $HOME/Library/Application Support/Gupax | /Users/Alice/Library/Application Support/Gupax + // Windows | {FOLDERID_RoamingAppData}\Gupax | C:\Users\Alice\AppData\Roaming\Gupax + match dirs::data_dir() { Some(mut path) => { path.push(DIRECTORY); - info!("OS | Data path ... OK"); - path + info!("OS | Data path ... OK ... {}", path.display()); + Ok(path) }, - None => { error!("Couldn't get OS PATH for data"); return Err(TomlError::Path(PATH_ERROR.to_string())) }, - }; + None => { error!("OS | Data path ... FAIL"); Err(TomlError::Path(PATH_ERROR.to_string())) }, + } +} + +pub fn create_gupax_dir(path: PathBuf) -> Result<(), TomlError> { // Create directory fs::create_dir_all(&path)?; - path.push(name); - info!("{:?} | Path ... {}", file, path.display()); - Ok(path) + Ok(()) } // Convert a [File] path to a [String] @@ -198,15 +184,14 @@ impl State { // |_ Create a default file if not found // 2. Deserialize [String] into a proper [Struct] // |_ Attempt to merge if deserialization fails - pub fn get() -> Result { + pub fn get(path: &PathBuf) -> Result { // Read let file = File::State; - let path = get_file_path(file)?; let string = match read_to_string(file, &path) { Ok(string) => string, // Create _ => { - Self::create_new()?; + Self::create_new(path)?; match read_to_string(file, &path) { Ok(s) => s, Err(e) => return Err(e), @@ -218,17 +203,16 @@ impl State { Ok(s) => Ok(s), Err(e) => { warn!("State | Attempting merge..."); - Self::merge(string) + Self::merge(string, path) }, } } // Completely overwrite current [state.toml] // with a new default version, and return [Self]. - pub fn create_new() -> Result { + pub fn create_new(path: &PathBuf) -> Result { info!("State | Creating new default..."); let new = Self::new(); - let path = get_file_path(File::State)?; let string = match toml::ser::to_string(&new) { Ok(o) => { info!("State | Serialization ... OK"); o }, Err(e) => { error!("State | Couldn't serialize default file: {}", e); return Err(TomlError::Serialize(e)) }, @@ -239,9 +223,8 @@ impl State { } // Save [State] onto disk file [gupax.toml] - pub fn save(&mut self) -> Result<(), TomlError> { + pub fn save(&mut self, path: &PathBuf) -> Result<(), TomlError> { info!("State | Saving to disk..."); - let path = get_file_path(File::State)?; // Convert path to absolute self.gupax.absolute_p2pool_path = into_absolute_path(self.gupax.p2pool_path.clone())?; self.gupax.absolute_xmrig_path = into_absolute_path(self.gupax.xmrig_path.clone())?; @@ -262,7 +245,7 @@ impl State { // Take [String] as input, merge it with whatever the current [default] is, // leaving behind old keys+values and updating [default] with old valid ones. // Automatically overwrite current file. - pub fn merge(old: String) -> Result { + pub fn merge(old: String, path: &PathBuf) -> Result { let default = match toml::ser::to_string(&Self::new()) { Ok(string) => { info!("State | Default TOML parse ... OK"); string }, Err(err) => { error!("State | Couldn't parse default TOML into string"); return Err(TomlError::Serialize(err)) }, @@ -272,7 +255,7 @@ impl State { Err(err) => { error!("State | Couldn't merge default + old TOML"); return Err(TomlError::Merge(err)) }, }; // Attempt save - Self::save(&mut new)?; + Self::save(&mut new, path)?; Ok(new) } } @@ -348,15 +331,14 @@ impl Node { // |_ Create a default file if not found // 2. Deserialize [String] into a proper [Struct] // |_ Attempt to merge if deserialization fails - pub fn get() -> Result, TomlError> { + pub fn get(path: &PathBuf) -> Result, TomlError> { // Read let file = File::Node; - let path = get_file_path(file)?; let string = match read_to_string(file, &path) { Ok(string) => string, // Create _ => { - Self::create_new()?; + Self::create_new(path)?; read_to_string(file, &path)? }, }; @@ -366,10 +348,9 @@ impl Node { // Completely overwrite current [node.toml] // with a new default version, and return [Vec]. - pub fn create_new() -> Result, TomlError> { + pub fn create_new(path: &PathBuf) -> Result, TomlError> { info!("Node | Creating new default..."); let new = Self::new_vec(); - let path = get_file_path(File::Node)?; let string = Self::to_string(&Self::new_vec()); fs::write(&path, &string)?; info!("Node | Write ... OK"); @@ -377,9 +358,8 @@ impl Node { } // Save [Node] onto disk file [node.toml] - pub fn save(vec: &Vec<(String, Self)>) -> Result<(), TomlError> { + pub fn save(vec: &Vec<(String, Self)>, path: &PathBuf) -> Result<(), TomlError> { info!("Node | Saving to disk..."); - let path = get_file_path(File::Node)?; let string = Self::to_string(vec); match fs::write(path, string) { Ok(_) => { info!("TOML save ... OK"); Ok(()) }, @@ -439,7 +419,7 @@ const PATH_ERROR: &'static str = "PATH for state directory could not be not foun #[cfg(target_os = "windows")] const DIRECTORY: &'static str = r#"Gupax\"#; #[cfg(target_os = "macos")] -const DIRECTORY: &'static str = "com.github.hinto-janaiyo.gupax/"; +const DIRECTORY: &'static str = "Gupax/"; #[cfg(target_os = "linux")] const DIRECTORY: &'static str = "gupax/"; diff --git a/src/gupax.rs b/src/gupax.rs index 66734f5..779e2cb 100644 --- a/src/gupax.rs +++ b/src/gupax.rs @@ -25,8 +25,11 @@ use egui::{ use crate::constants::*; use crate::disk::{Gupax,Version}; use crate::update::*; -use std::thread; -use std::sync::{Arc,Mutex}; +use std::{ + thread, + sync::{Arc,Mutex}, + path::PathBuf, +}; use log::*; //---------------------------------------------------------------------------------------------------- FileWindow @@ -55,7 +58,7 @@ impl FileWindow { //---------------------------------------------------------------------------------------------------- Gupax impl Gupax { - pub fn show(&mut self, og: &Arc>, state_ver: &Arc>, update: &Arc>, file_window: &Arc>, width: f32, height: f32, ctx: &egui::Context, ui: &mut egui::Ui) { + pub fn show(&mut self, og: &Arc>, state_ver: &Arc>, update: &Arc>, file_window: &Arc>, state_path: &PathBuf, width: f32, height: f32, ctx: &egui::Context, ui: &mut egui::Ui) { // Update button + Progress bar ui.group(|ui| { // These are in unnecessary [ui.vertical()]'s @@ -75,6 +78,7 @@ impl Gupax { let state_ver = Arc::clone(&state_ver); let update = Arc::clone(&update); let update_thread = Arc::clone(&update); + let state_path = state_path.clone(); thread::spawn(move|| { info!("Spawning update thread..."); match Update::start(update_thread, og.clone(), state_ver.clone()) { @@ -84,7 +88,7 @@ impl Gupax { }, _ => { info!("Update | Saving state..."); - match State::save(&mut og.lock().unwrap()) { + match State::save(&mut og.lock().unwrap(), &state_path) { Ok(_) => info!("Update ... OK"), Err(e) => { warn!("Update | Saving state ... FAIL ... {}", e); diff --git a/src/main.rs b/src/main.rs index f779404..7243b91 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,12 +37,14 @@ use env_logger::{Builder,WriteStyle}; use regex::Regex; // std -use std::io::Write; -use std::process::exit; -use std::sync::{Arc,Mutex}; -use std::{thread,env}; -use std::time::Instant; -use std::path::PathBuf; +use std::{ + io::Write, + process::exit, + sync::{Arc,Mutex}, + {thread,env}, + time::Instant, + path::PathBuf, +}; // Modules mod ferris; @@ -93,6 +95,8 @@ pub struct App { resolution: Vec2, // Frame resolution os: &'static str, // OS os_data_path: PathBuf, // OS data path (e.g: ~/.local/share/gupax) + state_path: PathBuf, // State file path + node_path: PathBuf, // Node file path version: &'static str, // Gupax version name_version: String, // [Gupax vX.X.X] img: Images, // Custom Struct holding pre-compiled bytes of [Images] @@ -111,7 +115,7 @@ impl App { fn new() -> Self { info!("Initializing App Struct..."); - let app = Self { + let mut app = Self { tab: Tab::default(), ping: Arc::new(Mutex::new(Ping::new())), width: 1280.0, @@ -128,68 +132,82 @@ impl App { xmrig: false, no_startup: false, now: Instant::now(), - exe: "".to_string(), - dir: "".to_string(), + exe: String::new(), + dir: String::new(), resolution: Vec2::new(1280.0, 720.0), os: OS, os_data_path: PathBuf::new(), + state_path: PathBuf::new(), + node_path: PathBuf::new(), version: GUPAX_VERSION, name_version: format!("Gupax {}", GUPAX_VERSION), img: Images::new(), regex: Regexes::new(), }; - // Apply arg state - let mut app = parse_args(app); + //---------------------------------------------------------------------------------------------------- App init data that *could* panic + let mut panic = String::new(); // Get exe path app.exe = match get_exe() { Ok(exe) => exe, - Err(err) => { panic_main(err.to_string()); exit(1); }, + Err(e) => { panic = format!("get_exe(): {}", e); app.error_state.set(panic.clone(), ErrorFerris::Panic, ErrorButtons::Quit); String::new() }, }; // Get exe directory path app.dir = match get_exe_dir() { Ok(dir) => dir, - Err(err) => { panic_main(err.to_string()); exit(1); }, + Err(e) => { panic = format!("get_exe_dir(): {}", e); app.error_state.set(panic.clone(), ErrorFerris::Panic, ErrorButtons::Quit); String::new() }, }; // Get OS data path - app.os_data_path = match get_os_data_path() { + app.os_data_path = match get_gupax_data_path() { Ok(dir) => dir, - Err(err) => { panic_main(err.to_string()); exit(1); }, + Err(e) => { panic = format!("get_os_data_path(): {}", e); app.error_state.set(panic.clone(), ErrorFerris::Panic, ErrorButtons::Quit); PathBuf::new() }, }; + + // Set [state.toml/node.toml] path + app.state_path = app.os_data_path.clone(); + app.state_path.push("state.toml"); + app.node_path = app.os_data_path.clone(); + app.node_path.push("node.toml"); + + // Apply arg state + // It's not safe to [--reset] if any of the previous variables + // are unset (null path), so make sure we just abort if the [panic] String contains something. + let mut app = parse_args(app, panic); + // Read disk state use TomlError::*; - app.state = match State::get() { + app.state = match State::get(&app.state_path) { Ok(toml) => toml, Err(err) => { error!("State ... {}", err); match err { - Io(e) => app.error_state.set(true, format!("State file: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), - Path(e) => app.error_state.set(true, format!("State file: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), - Serialize(e) => app.error_state.set(true, format!("State file: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), - Deserialize(e) => app.error_state.set(true, format!("State file: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), - Merge(e) => app.error_state.set(true, format!("State file: {}", e), ErrorFerris::Error, ErrorButtons::ResetState), + Io(e) => app.error_state.set(format!("State file: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), + Path(e) => app.error_state.set(format!("State file: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), + Serialize(e) => app.error_state.set(format!("State file: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), + Deserialize(e) => app.error_state.set(format!("State file: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), + Merge(e) => app.error_state.set(format!("State file: {}", e), ErrorFerris::Error, ErrorButtons::ResetState), }; State::new() }, }; app.og = Arc::new(Mutex::new(app.state.clone())); // Read node list - app.og_node_vec = match Node::get() { + app.og_node_vec = match Node::get(&app.node_path) { Ok(toml) => toml, Err(err) => { error!("Node ... {}", err); match err { - Io(e) => app.error_state.set(true, format!("Node list: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), - Path(e) => app.error_state.set(true, format!("Node list: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), - Serialize(e) => app.error_state.set(true, format!("Node list: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), - Deserialize(e) => app.error_state.set(true, format!("Node list: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), - Merge(e) => app.error_state.set(true, format!("Node list: {}", e), ErrorFerris::Error, ErrorButtons::ResetState), + Io(e) => app.error_state.set(format!("Node list: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), + Path(e) => app.error_state.set(format!("Node list: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), + Serialize(e) => app.error_state.set(format!("Node list: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), + Deserialize(e) => app.error_state.set(format!("Node list: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), + Merge(e) => app.error_state.set(format!("Node list: {}", e), ErrorFerris::Error, ErrorButtons::ResetState), }; Node::new_vec() }, }; app.node_vec = app.og_node_vec.clone(); - + //---------------------------------------------------------------------------------------------------- let mut og = app.og.lock().unwrap(); // Lock [og] // Handle max threads og.xmrig.max_threads = num_cpus::get(); @@ -259,10 +277,20 @@ impl ErrorState { } } - // Convenience function to set the [App] error state - pub fn set(&mut self, error: bool, msg: impl Into, ferris: ErrorFerris, buttons: ErrorButtons) { + // Convenience function to enable the [App] error state + pub fn set(&mut self, msg: impl Into, ferris: ErrorFerris, buttons: ErrorButtons) { + if self.error { + // If a panic error is already set, return + if self.ferris == ErrorFerris::Panic { return } + // If we shouldn't be overriding the current error, return + match self.buttons { + ErrorButtons::YesNo => (), // Not important + ErrorButtons::Okay => (), // Not important + _ => return, // Overwrite, Quits, etc + } + } *self = Self { - error, + error: true, msg: msg.into(), ferris, buttons, @@ -433,6 +461,7 @@ fn init_auto(app: &App) { let state_ver = Arc::clone(&app.state.version); let update = Arc::clone(&app.update); let update_thread = Arc::clone(&app.update); + let state_path = app.state_path.clone(); thread::spawn(move|| { info!("Spawning update thread..."); match Update::start(update_thread, og.clone(), state_ver.clone()) { @@ -442,7 +471,7 @@ fn init_auto(app: &App) { }, _ => { info!("Update | Saving state..."); - match State::save(&mut og.lock().unwrap()) { + match State::save(&mut og.lock().unwrap(), &state_path) { Ok(_) => info!("Update ... OK"), Err(e) => { warn!("Update | Saving state ... FAIL ... {}", e); @@ -472,41 +501,47 @@ fn init_auto(app: &App) { } } -fn reset_state() -> Result<(), TomlError> { +fn reset_state(path: &PathBuf) -> Result<(), TomlError> { info!("Resetting [state.toml]..."); - match State::create_new() { + match State::create_new(path) { Ok(_) => { info!("Resetting [state.toml] ... OK"); Ok(()) }, Err(e) => { error!("Resetting [state.toml] ... FAIL ... {}", e); Err(e) }, } } -fn reset_nodes() -> Result<(), TomlError> { +fn reset_nodes(path: &PathBuf) -> Result<(), TomlError> { info!("Resetting [node.toml]..."); - match Node::create_new() { + match Node::create_new(path) { Ok(_) => { info!("Resetting [node.toml] ... OK"); Ok(()) }, Err(e) => { error!("Resetting [node.toml] ... FAIL ... {}", e); Err(e) }, } } -fn reset() { +fn reset(path: &PathBuf, state: &PathBuf, node: &PathBuf) { let mut code = 0; - match reset_state() { + // Attempt to remove directory first + info!("OS data path ... {}", path.display()); + match std::fs::remove_dir_all(path) { + Ok(_) => info!("Removing OS data path ... OK"), + Err(e) => { error!("Removing OS data path ... FAIL ... {}", e); code = 1; }, + } + match reset_state(state) { Ok(_) => (), Err(_) => code = 1, } - match reset_nodes() { + match reset_nodes(node) { Ok(_) => (), Err(_) => code = 1, } match code { - 0 => println!("\nGupax files were reset successfully."), - _ => println!("\nGupax files reset FAILED."), + 0 => println!("\nGupax reset ... OK"), + _ => println!("\nGupax reset ... FAIL"), } exit(code); } //---------------------------------------------------------------------------------------------------- Misc functions -fn parse_args(mut app: App) -> App { +fn parse_args>(mut app: App, panic: S) -> App { info!("Parsing CLI arguments..."); let mut args: Vec = env::args().collect(); if args.len() == 1 { info!("No args ... OK"); return app } else { args.remove(0); info!("Args ... {:?}", args); } @@ -522,14 +557,21 @@ fn parse_args(mut app: App) -> App { _ => (), } } + // Abort on panic + let panic = panic.into(); + if ! panic.is_empty() { + info!("[Gupax error] {}", panic); + exit(1); + } + // Everything else for arg in args { match arg.as_str() { - "--nodes" => { info!("Printing node list..."); print_disk_file(File::Node); } - "--state" => { info!("Printing state..."); print_disk_file(File::State); } - "--reset-state" => if let Ok(()) = reset_state() { exit(0) } else { exit(1) }, - "--reset-nodes" => if let Ok(()) = reset_nodes() { exit(0) } else { exit(1) }, - "--reset-all" => reset(), + "--state" => { info!("Printing state..."); print_disk_file(&app.state_path); } + "--nodes" => { info!("Printing node list..."); print_disk_file(&app.node_path); } + "--reset-state" => if let Ok(()) = reset_state(&app.state_path) { exit(0) } else { exit(1) }, + "--reset-nodes" => if let Ok(()) = reset_nodes(&app.node_path) { exit(0) } else { exit(1) }, + "--reset-all" => reset(&app.os_data_path, &app.state_path, &app.node_path), "--no-startup" => app.no_startup = true, _ => { eprintln!("[Gupax error] Invalid option: [{}]\nFor help, use: [--help]", arg); exit(1); }, } @@ -541,7 +583,7 @@ fn parse_args(mut app: App) -> App { pub fn get_exe() -> Result { match std::env::current_exe() { Ok(path) => { Ok(path.display().to_string()) }, - Err(err) => { error!("Couldn't get exe basepath PATH"); return Err(err) }, + Err(err) => { error!("Couldn't get absolute Gupax PATH"); return Err(err) }, } } @@ -572,11 +614,7 @@ pub fn clean_dir() -> Result<(), anyhow::Error> { } // Print disk files to console -fn print_disk_file(file: File) { - let path = match get_file_path(file) { - Ok(path) => path, - Err(e) => { error!("{}", e); exit(1); }, - }; +fn print_disk_file(path: &PathBuf) { match std::fs::read_to_string(&path) { Ok(string) => { print!("{}", string); exit(0); }, Err(e) => { error!("{}", e); exit(1); }, @@ -652,7 +690,7 @@ fn main() { impl eframe::App for App { fn on_close_event(&mut self) -> bool { if self.state.gupax.ask_before_quit { - self.error_state.set(true, "", ErrorFerris::Oops, ErrorButtons::StayQuit); + self.error_state.set("", ErrorFerris::Oops, ErrorButtons::StayQuit); false } else { true @@ -749,36 +787,36 @@ impl eframe::App for App { // [Yes/No] buttons ResetState => { if ui.add_sized([width, height/2.0], egui::Button::new("Yes")).clicked() { - match reset_state() { + match reset_state(&self.state_path) { Ok(_) => { - match State::get() { + match State::get(&self.state_path) { Ok(s) => { self.state = s; self.og = Arc::new(Mutex::new(self.state.clone())); - self.error_state.set(true, "State read OK", ErrorFerris::Happy, ErrorButtons::Okay); + self.error_state.set("State read OK", ErrorFerris::Happy, ErrorButtons::Okay); }, - Err(e) => self.error_state.set(true, format!("State read fail: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), + Err(e) => self.error_state.set(format!("State read fail: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), } }, - Err(e) => self.error_state.set(true, format!("State reset fail: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), + Err(e) => self.error_state.set(format!("State reset fail: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), }; } if esc || ui.add_sized([width, height/2.0], egui::Button::new("No")).clicked() { self.error_state = ErrorState::new() } }, ResetNode => { if ui.add_sized([width, height/2.0], egui::Button::new("Yes")).clicked() { - match reset_nodes() { + match reset_nodes(&self.node_path) { Ok(_) => { - match Node::get() { + match Node::get(&self.node_path) { Ok(s) => { self.node_vec = s; self.og_node_vec = self.node_vec.clone(); - self.error_state.set(true, "Node read OK", ErrorFerris::Happy, ErrorButtons::Okay); + self.error_state.set("Node read OK", ErrorFerris::Happy, ErrorButtons::Okay); }, - Err(e) => self.error_state.set(true, format!("Node read fail: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), + Err(e) => self.error_state.set(format!("Node read fail: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), } }, - Err(e) => self.error_state.set(true, format!("Node reset fail: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), + Err(e) => self.error_state.set(format!("Node reset fail: {}", e), ErrorFerris::Panic, ErrorButtons::Quit), }; } if esc || ui.add_sized([width, height/2.0], egui::Button::new("No")).clicked() { self.error_state = ErrorState::new() } @@ -867,7 +905,7 @@ impl eframe::App for App { self.node_vec = self.og_node_vec.clone(); } if ui.add_sized([width, height], egui::Button::new("Save")).on_hover_text("Save changes").clicked() { - match self.state.save() { + match State::save(&mut self.state, &self.state_path) { Ok(_) => { let mut og = self.og.lock().unwrap(); og.gupax = self.state.gupax.clone(); @@ -875,12 +913,12 @@ impl eframe::App for App { og.xmrig = self.state.xmrig.clone(); }, Err(e) => { - self.error_state.set(true, format!("State file: {}", e), ErrorFerris::Error, ErrorButtons::Okay); + self.error_state.set(format!("State file: {}", e), ErrorFerris::Error, ErrorButtons::Okay); }, }; - match Node::save(&self.og_node_vec) { + match Node::save(&self.og_node_vec, &self.node_path) { Ok(_) => self.og_node_vec = self.node_vec.clone(), - Err(e) => self.error_state.set(true, format!("Node list: {}", e), ErrorFerris::Error, ErrorButtons::Okay), + Err(e) => self.error_state.set(format!("Node list: {}", e), ErrorFerris::Error, ErrorButtons::Okay), }; } }); @@ -979,7 +1017,7 @@ impl eframe::App for App { Status::show(self, self.width, self.height, ctx, ui); } Tab::Gupax => { - Gupax::show(&mut self.state.gupax, &self.og, &self.state.version, &self.update, &self.file_window, self.width, self.height, ctx, ui); + Gupax::show(&mut self.state.gupax, &self.og, &self.state.version, &self.update, &self.file_window, &self.state_path, self.width, self.height, ctx, ui); } Tab::P2pool => { P2pool::show(&mut self.state.p2pool, &mut self.node_vec, &self.og, self.p2pool, &self.ping, &self.regex, self.width, self.height, ctx, ui);