From 9fcb750ddcb5189ddf3e8c695ef95a13cdbc94fc Mon Sep 17 00:00:00 2001 From: hinto-janaiyo <hinto.janaiyo@protonmail.com> Date: Sun, 16 Oct 2022 17:29:24 -0400 Subject: [PATCH] add basic [--flags] --- src/constants.rs | 19 +++- src/main.rs | 242 +++++++++++++++++++++++++++++++---------------- src/node.rs | 89 +++++++---------- src/state.rs | 36 ++++--- 4 files changed, 236 insertions(+), 150 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 977adb5..4cda9a6 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. -// These are the versions bundled with Gupax. +pub const GUPAX_VERSION: &'static str = "v0.1.0"; pub const P2POOL_VERSION: &'static str = "v2.4"; pub const XMRIG_VERSION: &'static str = "v6.18.0"; @@ -23,6 +23,7 @@ pub const BYTES_ICON: &[u8] = include_bytes!("../images/png/icon.png"); pub const BYTES_BANNER: &[u8] = include_bytes!("../images/png/banner.png"); pub const P2POOL_BASE_ARGS: &'static str = ""; pub const XMRIG_BASE_ARGS: &'static str = "--http-host=127.0.0.1 --http-port=18088 --algo=rx/0 --coin=Monero --randomx-cache-qos"; +pub const HORIZONTAL: &'static str = "--------------------------------------------"; // OS specific #[cfg(target_os = "windows")] @@ -66,3 +67,19 @@ pub const XMRIG_NICEHASH: &'static str = "Enable nicehash.com support"; pub const XMRIG_KEEPALIVE: &'static str = "Send keepalived packet to prevent timeout (needs pool support)"; pub const XMRIG_THREADS: &'static str = "Number of CPU threads to use for mining"; pub const XMRIG_PRIORITY: &'static str = "Set process priority (0 idle, 2 normal to 5 highest)"; + +// CLI argument messages +pub const ARG_HELP: &'static str = +r#"USAGE: gupax [--flags] + + -h | --help Print this help message + -v | --version Print versions + -n | --no-startup Disable auto-update/node connections at startup + -r | --reset Reset all Gupax configuration/state"#; +pub const ARG_COPYRIGHT: &'static str = +r#"For more information: +https://github.com/hinto-janaiyo/gupax +https://github.com/SChernykh/p2pool +https://github.com/xmrig/xmrig + +Gupax, P2Pool, and XMRig are licensed under GPLv3."#; diff --git a/src/main.rs b/src/main.rs index cea0889..f84a2a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,8 +34,9 @@ use env_logger::{Builder,WriteStyle}; use std::io::Write; use std::process::exit; use std::sync::{Arc,Mutex}; -use std::thread; +use std::{thread,env}; use std::time::Instant; +use std::path::PathBuf; // Modules mod constants; @@ -55,10 +56,10 @@ use {constants::*,node::*,state::*,about::*,status::*,gupax::*,p2pool::*,xmrig:: pub struct App { // Misc state tab: Tab, // What tab are we on? - quit: bool, // Did user click quit button? - quit_confirm: bool, // Did user confirm to quit? - ping: bool, // Did user click the ping button? - ping_prog: Arc<Mutex<bool>>, // Are we in the progress of pinging? + quit: bool, // Was the quit button clicked? + quit_confirm: bool, // Was the quit confirmed? + ping: bool, // Was the ping button clicked? + pinging: Arc<Mutex<bool>>, // Is a ping in progress? node: Arc<Mutex<NodeStruct>>, // Data on community nodes // State: // og = Old state to compare against @@ -68,42 +69,58 @@ pub struct App { og: State, state: State, diff: bool, + // Process/update state: + // Doesn't make sense to save this on disk + // so it's represented as a bool here. + p2pool: bool, // Is p2pool online? + xmrig: bool, // Is xmrig online? + updating: bool, // Is an update in progress? + // State from [--flags] + startup: bool, + reset: bool, // Static stuff - now: Instant, - resolution: Vec2, - os: &'static str, - version: String, - name_version: String, - banner: RetainedImage, - - // TEMPORARY FIXME - p2pool: bool, - xmrig: bool, + now: Instant, // Internal timer + resolution: Vec2, // Frame resolution + os: &'static str, // OS + version: String, // Gupax version + name_version: String, // [Gupax vX.X.X] + banner: RetainedImage, // Gupax banner image } impl App { - fn new(cc: &eframe::CreationContext<'_>) -> Self { + fn cc(cc: &eframe::CreationContext<'_>, app: Self) -> Self { + let resolution = cc.integration_info.window_info.size; + init_text_styles(&cc.egui_ctx, resolution[0] as f32); Self { + resolution, + ..app + } + } + + fn default() -> Self { + let app = Self { tab: Tab::default(), quit: false, quit_confirm: false, ping: false, - ping_prog: Arc::new(Mutex::new(false)), + pinging: Arc::new(Mutex::new(false)), node: Arc::new(Mutex::new(NodeStruct::default())), og: State::default(), state: State::default(), diff: false, + p2pool: false, + xmrig: false, + updating: false, + startup: true, + reset: false, now: Instant::now(), - resolution: cc.integration_info.window_info.size, + resolution: Vec2::new(1280.0, 720.0), os: OS, version: "v0.0.1".to_string(), name_version: "Gupax v0.0.1".to_string(), banner: RetainedImage::from_image_bytes("banner.png", BYTES_BANNER).expect("oops"), - - // TEMPORARY FIXME - p2pool: false, - xmrig: false, - } + }; + parse_args(app) } } @@ -153,6 +170,10 @@ fn init_text_styles(ctx: &egui::Context, width: f32) { } fn init_logger() { + #[cfg(debug_assertions)] + let filter = LevelFilter::Info; + #[cfg(not(debug_assertions))] + let filter = LevelFilter::Warn; use env_logger::fmt::Color; Builder::new().format(|buf, record| { let level; @@ -173,7 +194,7 @@ fn init_logger() { buf.style().set_dimmed(true).value(record.line().unwrap_or(0)), record.args(), ) - }).filter_level(LevelFilter::Info).write_style(WriteStyle::Always).parse_default_env().format_timestamp_millis().init(); + }).filter_level(filter).write_style(WriteStyle::Always).parse_default_env().format_timestamp_millis().init(); info!("init_logger() ... OK"); } @@ -195,6 +216,45 @@ fn init_options() -> NativeOptions { options } +//---------------------------------------------------------------------------------------------------- Misc functions +fn into_absolute_path(path: String) -> Result<PathBuf, std::io::Error> { + let path = PathBuf::from(path); + if path.is_relative() { + let mut dir = std::env::current_exe()?; + dir.pop(); + dir.push(path); + Ok(dir) + } else { + Ok(path) + } +} + +fn parse_args(mut app: App) -> App { + info!("Parsing CLI arguments..."); + let mut args: Vec<String> = env::args().collect(); + if args.len() == 1 { info!("No args ... OK"); return app } else { args.remove(0); info!("Args ... {:?}", args); } + // [help/version], exit early + for arg in &args { + match arg.as_str() { + "-h"|"--help" => { println!("{}", ARG_HELP); exit(0); }, + "-v"|"--version" => { + println!("Gupax | {}\nP2Pool | {}\nXMRig | {}\n\n{}", GUPAX_VERSION, P2POOL_VERSION, XMRIG_VERSION, ARG_COPYRIGHT); + exit(0); + }, + _ => (), + } + } + // Everything else + for arg in args { + match arg.as_str() { + "-n"|"--no-startup" => { info!("Disabling startup..."); app.startup = false; } + "-r"|"--reset" => { info!("Resetting state..."); app.reset = true; } + _ => { eprintln!("[Gupax error] Invalid option: [{}]\nFor help, use: [--help]", arg); exit(1); }, + } + } + app +} + //---------------------------------------------------------------------------------------------------- [App] frame for [Panic] situations struct Panic { error_msg: String, } impl Panic { @@ -240,19 +300,19 @@ impl eframe::App for Panic { //---------------------------------------------------------------------------------------------------- Main [App] frame fn main() { init_logger(); -// let toml = match State::get() { -// Ok(toml) => toml, -// Err(err) => { -// error!("{}", err); -// let error_msg = err.to_string(); -// let options = Panic::options(); -// eframe::run_native("Gupax", options, Box::new(|cc| Box::new(Panic::new(cc, error_msg))),); -// exit(1); -// }, -// }; - let state = State::default(); + let app = App::default(); let options = init_options(); - eframe::run_native("Gupax", options, Box::new(|cc| Box::new(App::new(cc))),); + let toml = match State::get() { + Ok(toml) => toml, + Err(err) => { + error!("{}", err); + let error_msg = err.to_string(); + let options = Panic::options(); + eframe::run_native("Gupax", options, Box::new(|cc| Box::new(Panic::new(cc, error_msg))),); + exit(1); + }, + }; + eframe::run_native("Gupax", options, Box::new(|cc| Box::new(App::cc(cc, app))),); } impl eframe::App for App { @@ -262,49 +322,75 @@ impl eframe::App for App { } fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { - init_text_styles(ctx, 1280.0); +// init_text_styles(ctx, 1280.0); // Close confirmation. if self.quit { egui::CentralPanel::default().show(ctx, |ui| { + init_text_styles(ctx, ui.available_width()); let width = ui.available_width(); let width = width - 10.0; let height = ui.available_height(); - init_text_styles(ctx, width); - ui.add_sized([width, height/2.0], Label::new("Are you sure you want to quit?")); - ui.group(|ui| { - if ui.add_sized([width, height/10.0], egui::Button::new("Yes")).clicked() { - info!("Quit confirmation = yes ... goodbye!"); - self.quit_confirm = true; - frame.close(); - } else if ui.add_sized([width, height/10.0], egui::Button::new("No")).clicked() { - self.quit = false; + // Detect processes or update + if self.p2pool || self.xmrig { + ui.add_sized([width, height/6.0], Label::new("Are you sure you want to quit?")); + if self.p2pool { ui.add_sized([width, height/6.0], Label::new("P2Pool is online...!")); } + if self.xmrig { ui.add_sized([width, height/6.0], Label::new("XMRig is online...!")); } + // Else, just quit + } else { + if self.state.gupax.save_before_quit { + info!("Saving before quit..."); + match self.state.save() { + Err(err) => { error!("{}", err); exit(1); }, + _ => (), + }; } + info!("No processes or update in progress ... goodbye!"); + exit(0); + } + egui::TopBottomPanel::bottom("quit").show(ctx, |ui| { + ui.group(|ui| { + if ui.add_sized([width, height/8.0], egui::Button::new("Yes")).clicked() { + if self.state.gupax.save_before_quit { + info!("Saving before quit..."); + match self.state.save() { + Err(err) => { error!("{}", err); exit(1); }, + _ => (), + }; + } + info!("Quit confirmation = yes ... goodbye!"); + self.quit_confirm = true; + exit(0); + } else if ui.add_sized([width, height/8.0], egui::Button::new("No")).clicked() { + self.quit = false; + } + }); }); }); return } - // + // If ping was pressed, start thread if self.ping { self.ping = false; - self.ping_prog = Arc::new(Mutex::new(true)); + self.pinging = Arc::new(Mutex::new(true)); let node_clone = Arc::clone(&self.node); - let prog_clone = Arc::clone(&self.ping_prog); + let pinging_clone = Arc::clone(&self.pinging); thread::spawn(move|| { let result = NodeStruct::ping(); - *node_clone.lock().unwrap() = result.node; - *prog_clone.lock().unwrap() = false; + *node_clone.lock().unwrap() = result.nodes; + *pinging_clone.lock().unwrap() = false; }); } -// if *self.ping_prog.lock().unwrap() { + // If ping-ING, display stats +// if *self.pinging.lock().unwrap() { // egui::CentralPanel::default().show(ctx, |ui| { // let width = ui.available_width(); // let width = width - 10.0; // let height = ui.available_height(); // init_text_styles(ctx, width); -// ui.add_sized([width, height/2.0], Label::new(format!("In progress: {}", *self.ping_prog.lock().unwrap()))); +// ui.add_sized([width, height/2.0], Label::new(format!("In progress: {}", *self.pinging.lock().unwrap()))); // ui.group(|ui| { // if ui.add_sized([width, height/10.0], egui::Button::new("Yes")).clicked() { // info!("Quit confirmation = yes ... goodbye!"); @@ -324,8 +410,8 @@ impl eframe::App for App { let width = (ui.available_width() - 90.0) / 5.0; let height = ui.available_height() / 10.0; ui.add_space(4.0); - ui.style_mut().override_text_style = Some(Name("Tab".into())); ui.horizontal(|ui| { + ui.style_mut().override_text_style = Some(Name("Tab".into())); ui.style_mut().visuals.widgets.inactive.fg_stroke.color = Color32::from_rgb(100, 100, 100); ui.style_mut().visuals.selection.bg_fill = Color32::from_rgb(255, 120, 120); ui.style_mut().visuals.selection.stroke = Stroke { @@ -346,7 +432,6 @@ impl eframe::App for App { ui.separator(); // }); - let height = height / 2.0; // Bottom: app info + state/process buttons egui::TopBottomPanel::bottom("bottom").show(ctx, |ui| { @@ -359,9 +444,6 @@ impl eframe::App for App { ui.add_sized([width, height], Label::new(self.os)); ui.separator(); ui.add_sized([width/1.5, height], Label::new("P2Pool")); -// TODO -// self.p2pool + self.xmrig -// This is for process online/offline status if self.p2pool == true { ui.add_sized([width/4.0, height], Label::new(RichText::new("⏺").color(Color32::from_rgb(100, 230, 100)))); } else { @@ -428,31 +510,25 @@ impl eframe::App for App { }); }); - // Central: tab contents - // Docs say to add central last, don't think it matters here but whatever: - // [https://docs.rs/egui/latest/egui/containers/panel/struct.TopBottomPanel.html] -// egui::TopBottomPanel::bottom("2").show(ctx, |ui| { - ui.style_mut().override_text_style = Some(egui::TextStyle::Body); - match self.tab { - Tab::About => { - About::show(self, ctx, ui); - } - Tab::Status => { - Status::show(self, ctx, ui); - } - Tab::Gupax => { -// Gupax::show(self.state.gupax, ctx, ui); - exit(0); - } - Tab::P2pool => { - P2pool::show(&mut self.state.p2pool, ctx, ui); - } - Tab::Xmrig => { - Xmrig::show(&mut self.state.xmrig, ctx, ui); - } - } -// }); - + ui.style_mut().override_text_style = Some(egui::TextStyle::Body); + match self.tab { + Tab::About => { + About::show(self, ctx, ui); + } + Tab::Status => { + Status::show(self, ctx, ui); + } + Tab::Gupax => { +// Gupax::show(self.state.gupax, ctx, ui); + exit(0); + } + Tab::P2pool => { + P2pool::show(&mut self.state.p2pool, ctx, ui); + } + Tab::Xmrig => { + Xmrig::show(&mut self.state.xmrig, ctx, ui); + } + } }); } } diff --git a/src/node.rs b/src/node.rs index 9f2145a..85732f5 100644 --- a/src/node.rs +++ b/src/node.rs @@ -41,6 +41,11 @@ pub const SUPPORTXMR: &'static str = "node.supportxmr.com:18081"; pub const SUPPORTXMR_IR: &'static str = "node.supportxmr.ir:18081"; pub const XMRVSBEAST: &'static str = "p2pmd.xmrvsbeast.com:18081"; +pub const NODE_IPS: [&'static str; 12] = [ + C3POOL,CAKE,CAKE_EU,CAKE_UK,CAKE_US,MONERUJO,RINO, + SELSTA,SETH,SUPPORTXMR,SUPPORTXMR_IR,XMRVSBEAST, +]; + #[derive(Debug)] pub struct NodeStruct { c3pool: Data, cake: Data, cake_eu: Data, cake_uk: Data, cake_us: Data, monerujo: Data, @@ -52,7 +57,7 @@ pub struct Data { pub ms: u128, pub color: Color32, pub id: NodeEnum, - pub ip: String, + pub ip: &'static str, } #[derive(Copy,Clone,Eq,PartialEq,Debug,Deserialize,Serialize)] @@ -69,39 +74,31 @@ impl std::fmt::Display for NodeEnum { #[derive(Debug)] pub struct PingResult { - pub node: NodeStruct, + pub nodes: NodeStruct, pub fastest: NodeEnum, } +use crate::NodeEnum::*; impl NodeStruct { pub fn default() -> Self { - use crate::NodeEnum::*; let ms = 0; let color = Color32::GRAY; Self { - c3pool: Data { ms, color, id: C3pool, ip: C3POOL.to_string(), }, - cake: Data { ms, color, id: Cake, ip: CAKE.to_string(), }, - cake_eu: Data { ms, color, id: CakeEu, ip: CAKE_EU.to_string(), }, - cake_uk: Data { ms, color, id: CakeUk, ip: CAKE_UK.to_string(), }, - cake_us: Data { ms, color, id: CakeUs, ip: CAKE_US.to_string(), }, - monerujo: Data { ms, color, id: Monerujo, ip: MONERUJO.to_string(), }, - rino: Data { ms, color, id: Rino, ip: RINO.to_string(), }, - selsta: Data { ms, color, id: Selsta, ip: SELSTA.to_string(), }, - seth: Data { ms, color, id: Seth, ip: SETH.to_string(), }, - supportxmr: Data { ms, color, id: SupportXmr, ip: SUPPORTXMR.to_string(), }, - supportxmr_ir: Data { ms, color, id: SupportXmrIr, ip: SUPPORTXMR_IR.to_string(), }, - xmrvsbeast: Data { ms, color, id: XmrVsBeast, ip: XMRVSBEAST.to_string(), }, + c3pool: Data { ms, color, id: C3pool, ip: C3POOL, }, + cake: Data { ms, color, id: Cake, ip: CAKE, }, + cake_eu: Data { ms, color, id: CakeEu, ip: CAKE_EU, }, + cake_uk: Data { ms, color, id: CakeUk, ip: CAKE_UK, }, + cake_us: Data { ms, color, id: CakeUs, ip: CAKE_US, }, + monerujo: Data { ms, color, id: Monerujo, ip: MONERUJO, }, + rino: Data { ms, color, id: Rino, ip: RINO, }, + selsta: Data { ms, color, id: Selsta, ip: SELSTA, }, + seth: Data { ms, color, id: Seth, ip: SETH, }, + supportxmr: Data { ms, color, id: SupportXmr, ip: SUPPORTXMR, }, + supportxmr_ir: Data { ms, color, id: SupportXmrIr, ip: SUPPORTXMR_IR, }, + xmrvsbeast: Data { ms, color, id: XmrVsBeast, ip: XMRVSBEAST, }, } } - // Return array of all IPs - fn array() -> [&'static str; 12] { - [ - C3POOL,CAKE,CAKE_EU,CAKE_UK,CAKE_US,MONERUJO,RINO, - SELSTA,SETH,SUPPORTXMR,SUPPORTXMR_IR,XMRVSBEAST, - ] - } - // This is for pinging the community nodes to // find the fastest/slowest one for the user. // The process: @@ -123,9 +120,8 @@ impl NodeStruct { // timeout = BLACK // default = GRAY pub fn ping() -> PingResult { - use crate::NodeEnum::*; info!("Starting community node pings..."); - let mut node = NodeStruct::default(); + let mut nodes = NodeStruct::default(); let mut get_info = HashMap::new(); get_info.insert("jsonrpc", "2.0"); get_info.insert("id", "0"); @@ -134,7 +130,7 @@ impl NodeStruct { let fastest = false; let timeout_sec = Duration::from_millis(5000); - for ip in Self::array().iter() { + for ip in NODE_IPS.iter() { let id = match *ip { C3POOL => C3pool, CAKE => Cake, @@ -182,18 +178,18 @@ impl NodeStruct { color = Color32::LIGHT_GREEN } match id { - C3pool => { node.c3pool.ms = ms; node.c3pool.color = color; }, - Cake => { node.cake.ms = ms; node.cake.color = color; }, - CakeEu => { node.cake_eu.ms = ms; node.cake_eu.color = color; }, - CakeUk => { node.cake_uk.ms = ms; node.cake_uk.color = color; }, - CakeUs => { node.cake_us.ms = ms; node.cake_us.color = color; }, - Monerujo => { node.monerujo.ms = ms; node.monerujo.color = color; }, - Rino => { node.rino.ms = ms; node.rino.color = color; }, - Selsta => { node.selsta.ms = ms; node.selsta.color = color; }, - Seth => { node.seth.ms = ms; node.seth.color = color; }, - SupportXmr => { node.supportxmr.ms = ms; node.supportxmr.color = color; }, - SupportXmrIr => { node.supportxmr_ir.ms = ms; node.supportxmr_ir.color = color; }, - XmrVsBeast => { node.xmrvsbeast.ms = ms; node.xmrvsbeast.color = color; }, + C3pool => { nodes.c3pool.ms = ms; nodes.c3pool.color = color; }, + Cake => { nodes.cake.ms = ms; nodes.cake.color = color; }, + CakeEu => { nodes.cake_eu.ms = ms; nodes.cake_eu.color = color; }, + CakeUk => { nodes.cake_uk.ms = ms; nodes.cake_uk.color = color; }, + CakeUs => { nodes.cake_us.ms = ms; nodes.cake_us.color = color; }, + Monerujo => { nodes.monerujo.ms = ms; nodes.monerujo.color = color; }, + Rino => { nodes.rino.ms = ms; nodes.rino.color = color; }, + Selsta => { nodes.selsta.ms = ms; nodes.selsta.color = color; }, + Seth => { nodes.seth.ms = ms; nodes.seth.color = color; }, + SupportXmr => { nodes.supportxmr.ms = ms; nodes.supportxmr.color = color; }, + SupportXmrIr => { nodes.supportxmr_ir.ms = ms; nodes.supportxmr_ir.color = color; }, + XmrVsBeast => { nodes.xmrvsbeast.ms = ms; nodes.xmrvsbeast.color = color; }, } } let mut best_ms: u128 = vec[0].0; @@ -208,22 +204,7 @@ impl NodeStruct { // The values don't update if not printed beforehand, // so the match below on [fastest] gets funky. info!("Fastest node ... {:#?} @ {:#?}ms", fastest, best_ms); - let ip = match fastest { - C3pool => C3POOL, - Cake => CAKE, - CakeEu => CAKE_EU, - CakeUk => CAKE_UK, - CakeUs => CAKE_US, - Monerujo => MONERUJO, - Rino => RINO, - Selsta => SELSTA, - Seth => SETH, - SupportXmr => SUPPORTXMR, - SupportXmrIr => SUPPORTXMR_IR, - XmrVsBeast => XMRVSBEAST, - }; - info!("Using IP ... {}", ip); info!("Community node ping ... OK"); - PingResult { node, fastest, } + PingResult { nodes, fastest, } } } diff --git a/src/state.rs b/src/state.rs index 712f384..4a8366e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -33,6 +33,7 @@ use std::fmt::Display; use std::path::{Path,PathBuf}; use std::result::Result; use serde_derive::{Serialize,Deserialize}; +use crate::constants::HORIZONTAL; use log::*; //---------------------------------------------------------------------------------------------------- Impl @@ -43,6 +44,7 @@ impl State { gupax: Gupax { auto_update: true, ask_before_quit: true, + save_before_quit: true, p2pool_path: DEFAULT_P2POOL_PATH.to_string(), xmrig_path: DEFAULT_XMRIG_PATH.to_string(), }, @@ -107,14 +109,14 @@ impl State { Ok(string) }, Err(err) => { - error!("TOML not found, attempting to create default"); + warn!("TOML not found, attempting to create default"); let default = match toml::ser::to_string(&State::default()) { Ok(o) => { info!("TOML serialization ... OK"); o }, Err(e) => { error!("Couldn't serialize default TOML file: {}", e); return Err(TomlError::Serialize(e)) }, }; fs::write(&path, &default)?; info!("TOML write ... OK"); - Ok(fs::read_to_string(default)?) + Ok(default) }, } } @@ -124,7 +126,9 @@ impl State { match toml::de::from_str(&string) { Ok(toml) => { info!("TOML parse ... OK"); - eprint!("{}", string); + info!("{}", HORIZONTAL); + for i in string.lines() { info!("{}", i); } + info!("{}", HORIZONTAL); Ok(toml) }, Err(err) => { error!("Couldn't parse TOML from string"); Err(TomlError::Deserialize(err)) }, @@ -133,22 +137,25 @@ impl State { // Last three functions combined // get_path() -> read_or_create() -> parse() -// pub fn get() -> Result<State, TomlError> { -// let path = Self::path(); -// } + pub fn get() -> Result<State, TomlError> { + Self::parse(Self::read_or_create(Self::get_path()?)?) + } - // Overwrite disk Toml with memory State (save state) - pub fn overwrite(state: State, path: PathBuf) -> Result<(), TomlError> { + // Save [State] onto disk file [gupax.toml] + pub fn save(&self) -> Result<(), TomlError> { + let path = Self::get_path()?; info!("Starting TOML overwrite..."); - let string = match toml::ser::to_string(&state) { + let string = match toml::ser::to_string(&self) { Ok(string) => { info!("TOML parse ... OK"); - eprint!("{}", string); + info!("{}", HORIZONTAL); + for i in string.lines() { info!("{}", i); } + info!("{}", HORIZONTAL); string }, Err(err) => { error!("Couldn't parse TOML into string"); return Err(TomlError::Serialize(err)) }, }; - match fs::write(&path, string) { + match fs::write(path, string) { Ok(_) => { info!("TOML overwrite ... OK"); Ok(()) }, Err(err) => { error!("Couldn't overwrite TOML file"); return Err(TomlError::Io(err)) }, } @@ -176,7 +183,7 @@ impl From<std::io::Error> for TomlError { //---------------------------------------------------------------------------------------------------- Const const FILENAME: &'static str = "gupax.toml"; const ERROR: &'static str = "TOML Error"; - const PATH_ERROR: &'static str = "PATH for state directory could not be not found"; +const PATH_ERROR: &'static str = "PATH for state directory could not be not found"; #[cfg(target_os = "windows")] const DIRECTORY: &'static str = "Gupax"; #[cfg(target_os = "macos")] @@ -218,6 +225,7 @@ pub struct State { pub struct Gupax { pub auto_update: bool, pub ask_before_quit: bool, + pub save_before_quit: bool, pub p2pool_path: String, pub xmrig_path: String, } @@ -234,6 +242,8 @@ pub struct P2pool { pub rpc: u16, pub zmq: u16, pub address: String, +// pub config: String, +// pub args: String, } #[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)] @@ -248,6 +258,8 @@ pub struct Xmrig { pub priority: u8, pub pool: String, pub address: String, +// pub config: String, +// pub args: String, } #[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)]