main: implement [ErrorState] checks in main()

This commit is contained in:
hinto-janaiyo 2022-11-16 14:07:27 -05:00
parent a13e6d689b
commit 50fff5c311
No known key found for this signature in database
GPG key ID: B1C5A64B80691E45
4 changed files with 113 additions and 121 deletions

View file

@ -60,11 +60,11 @@ pub const HUGEPAGES_1GB: bool = true;
// Tooltips // Tooltips
// Gupax // Gupax
pub const GUPAX_UPDATE: &'static str = "Check for update on Gupax, P2Pool, and XMRig via GitHub's API and upgrade automatically"; pub const GUPAX_UPDATE: &'static str = "Check for updates on Gupax, P2Pool, and XMRig via GitHub's API and upgrade automatically";
pub const GUPAX_AUTO_UPDATE: &'static str = "Automatically check for updates at startup"; pub const GUPAX_AUTO_UPDATE: &'static str = "Automatically check for updates at startup";
pub const GUPAX_UPDATE_VIA_TOR: &'static str = "Update through the Tor network. Tor is embedded within Gupax; a Tor system proxy is not required"; pub const GUPAX_UPDATE_VIA_TOR: &'static str = "Update through the Tor network. Tor is embedded within Gupax; a Tor system proxy is not required";
pub const GUPAX_AUTO_NODE: &'static str = "Automatically ping the community Monero nodes and select the fastest at startup for P2Pool"; pub const GUPAX_AUTO_NODE: &'static str = "Automatically ping the community Monero nodes and select the fastest at startup for P2Pool";
pub const GUPAX_ASK_BEFORE_QUIT: &'static str = "Ask before quitting if processes are still alive or if an update is in progress"; pub const GUPAX_ASK_BEFORE_QUIT: &'static str = "Ask before quitting Gupax";
pub const GUPAX_SAVE_BEFORE_QUIT: &'static str = "Automatically save any changed settings before quitting"; pub const GUPAX_SAVE_BEFORE_QUIT: &'static str = "Automatically save any changed settings before quitting";
pub const GUPAX_PATH_P2POOL: &'static str = "The location of the P2Pool binary: Both absolute and relative paths are accepted; A red [X] will appear if there is no file found at the given path"; pub const GUPAX_PATH_P2POOL: &'static str = "The location of the P2Pool binary: Both absolute and relative paths are accepted; A red [X] will appear if there is no file found at the given path";
pub const GUPAX_PATH_XMRIG: &'static str = "The location of the XMRig binary: Both absolute and relative paths are accepted; A red [X] will appear if there is no file found at the given path"; pub const GUPAX_PATH_XMRIG: &'static str = "The location of the XMRig binary: Both absolute and relative paths are accepted; A red [X] will appear if there is no file found at the given path";
@ -94,9 +94,9 @@ r#"Use advanced settings:
- Out/In peer setting - Out/In peer setting
- Log level setting"#; - Log level setting"#;
pub const P2POOL_NAME: &'static str = "Add a unique name to identify this node; Only [A-Za-z0-9-_] and spaces allowed; Max length = 30 characters"; pub const P2POOL_NAME: &'static str = "Add a unique name to identify this node; Only [A-Za-z0-9-_] and spaces allowed; Max length = 30 characters";
pub const P2POOL_NODE_IP: &'static str = "Specify the Monero Node IP to connect to with P2Pool; Max length = 255 characters"; pub const P2POOL_NODE_IP: &'static str = "Specify the Monero Node IP to connect to with P2Pool; It must be a valid IPv4 address or a valid domain name; Max length = 255 characters";
pub const P2POOL_RPC_PORT: &'static str = "Specify the RPC port of the Monero node; [0-65535]"; pub const P2POOL_RPC_PORT: &'static str = "Specify the RPC port of the Monero node; [1-65535]";
pub const P2POOL_ZMQ_PORT: &'static str = "Specify the ZMQ port of the Monero node; [0-65535]"; pub const P2POOL_ZMQ_PORT: &'static str = "Specify the ZMQ port of the Monero node; [1-65535]";
pub const P2POOL_ADD: &'static str = "Add the current values to the list"; pub const P2POOL_ADD: &'static str = "Add the current values to the list";
pub const P2POOL_DELETE: &'static str = "Delete the currently selected node"; pub const P2POOL_DELETE: &'static str = "Delete the currently selected node";
pub const P2POOL_CLEAR: &'static str = "Clear all current values"; pub const P2POOL_CLEAR: &'static str = "Clear all current values";

View file

@ -50,11 +50,24 @@ use log::*;
// create_new() | Write a default TOML Struct into the appropriate file (in OS data path) // create_new() | Write a default TOML Struct into the appropriate file (in OS data path)
// into_absolute_path() | Convert relative -> absolute path // into_absolute_path() | Convert relative -> absolute path
pub fn get_file_path(file: File) -> Result<PathBuf, TomlError> { pub fn get_os_data_path() -> Result<PathBuf, TomlError> {
// Get OS data folder // Get OS data folder
// Linux | $XDG_DATA_HOME or $HOME/.local/share | /home/alice/.local/state // Linux | $XDG_DATA_HOME or $HOME/.local/share | /home/alice/.local/state
// macOS | $HOME/Library/Application Support | /Users/Alice/Library/Application Support // macOS | $HOME/Library/Application Support | /Users/Alice/Library/Application Support
// Windows | {FOLDERID_RoamingAppData} | C:\Users\Alice\AppData\Roaming // Windows | {FOLDERID_RoamingAppData} | C:\Users\Alice\AppData\Roaming
let mut path = match dirs::data_dir() {
Some(mut 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<PathBuf, TomlError> {
let name = File::name(&file); let name = File::name(&file);
let mut path = match dirs::data_dir() { let mut path = match dirs::data_dir() {
@ -217,22 +230,22 @@ impl State {
// Save [State] onto disk file [gupax.toml] // Save [State] onto disk file [gupax.toml]
pub fn save(&mut self) -> Result<(), TomlError> { pub fn save(&mut self) -> Result<(), TomlError> {
info!("Saving {:?} to disk...", self); info!("State | Saving to disk...");
let path = get_file_path(File::State)?; let path = get_file_path(File::State)?;
// Convert path to absolute // Convert path to absolute
self.gupax.absolute_p2pool_path = into_absolute_path(self.gupax.p2pool_path.clone())?; 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())?; self.gupax.absolute_xmrig_path = into_absolute_path(self.gupax.xmrig_path.clone())?;
let string = match toml::ser::to_string(&self) { let string = match toml::ser::to_string(&self) {
Ok(string) => { Ok(string) => {
info!("TOML parse ... OK"); info!("State | Parse ... OK");
print_toml(&string); print_toml(&string);
string string
}, },
Err(err) => { error!("Couldn't parse TOML into string"); return Err(TomlError::Serialize(err)) }, Err(err) => { error!("State | Couldn't parse TOML into string ... FAIL"); return Err(TomlError::Serialize(err)) },
}; };
match fs::write(path, string) { match fs::write(path, string) {
Ok(_) => { info!("TOML save ... OK"); Ok(()) }, Ok(_) => { info!("State | Save ... OK"); Ok(()) },
Err(err) => { error!("Couldn't overwrite TOML file"); return Err(TomlError::Io(err)) }, Err(err) => { error!("State | Couldn't overwrite TOML file ... FAIL"); return Err(TomlError::Io(err)) },
} }
} }

View file

@ -64,8 +64,7 @@ use {ferris::*,constants::*,node::*,disk::*,status::*,gupax::*,p2pool::*,xmrig::
pub struct App { pub struct App {
// Misc state // Misc state
tab: Tab, // What tab are we on? tab: Tab, // What tab are we on?
quit: bool, // Was the quit button clicked? // quit: bool, // Was the quit confirmed?
quit_confirm: bool, // Was the quit confirmed?
ping: Arc<Mutex<Ping>>, // Ping data found in [node.rs] ping: Arc<Mutex<Ping>>, // Ping data found in [node.rs]
width: f32, // Top-level width width: f32, // Top-level width
height: f32, // Top-level height height: f32, // Top-level height
@ -95,9 +94,10 @@ pub struct App {
dir: String, // Directory [Gupax] binary is in dir: String, // Directory [Gupax] binary is in
resolution: Vec2, // Frame resolution resolution: Vec2, // Frame resolution
os: &'static str, // OS os: &'static str, // OS
os_data_path: PathBuf, // OS data path (e.g: ~/.local/share/gupax)
version: &'static str, // Gupax version version: &'static str, // Gupax version
name_version: String, // [Gupax vX.X.X] name_version: String, // [Gupax vX.X.X]
image: Images, // Custom Struct holding pre-compiled bytes of [Images] img: Images, // Custom Struct holding pre-compiled bytes of [Images]
regex: Regexes, // Custom Struct holding pre-made [Regex]'s regex: Regexes, // Custom Struct holding pre-made [Regex]'s
} }
@ -114,8 +114,7 @@ impl App {
fn new() -> Self { fn new() -> Self {
let app = Self { let app = Self {
tab: Tab::default(), tab: Tab::default(),
quit: false, // quit: false,
quit_confirm: false,
ping: Arc::new(Mutex::new(Ping::new())), ping: Arc::new(Mutex::new(Ping::new())),
width: 1280.0, width: 1280.0,
height: 720.0, height: 720.0,
@ -135,9 +134,10 @@ impl App {
dir: "".to_string(), dir: "".to_string(),
resolution: Vec2::new(1280.0, 720.0), resolution: Vec2::new(1280.0, 720.0),
os: OS, os: OS,
os_data_path: PathBuf::new(),
version: GUPAX_VERSION, version: GUPAX_VERSION,
name_version: format!("Gupax {}", GUPAX_VERSION), name_version: format!("Gupax {}", GUPAX_VERSION),
image: Images::new(), img: Images::new(),
regex: Regexes::new(), regex: Regexes::new(),
}; };
// Apply arg state // Apply arg state
@ -152,6 +152,11 @@ impl App {
Ok(dir) => dir, Ok(dir) => dir,
Err(err) => { panic_main(err.to_string()); exit(1); }, Err(err) => { panic_main(err.to_string()); exit(1); },
}; };
// Get OS data path
app.os_data_path = match get_os_data_path() {
Ok(dir) => dir,
Err(err) => { panic_main(err.to_string()); exit(1); },
};
// Read disk state if no [--reset] arg // Read disk state if no [--reset] arg
if app.reset == false { if app.reset == false {
app.og = match State::get() { app.og = match State::get() {
@ -200,8 +205,8 @@ impl Default for Tab {
//---------------------------------------------------------------------------------------------------- [ErrorState] struct //---------------------------------------------------------------------------------------------------- [ErrorState] struct
pub struct ErrorState { pub struct ErrorState {
error: bool, // Is there an error? error: bool, // Is there an error?
msg: String, // What message to display? msg: &'static str, // What message to display?
image: RetainedImage, // Which ferris to display? ferris: ErrorFerris, // Which ferris to display?
buttons: ErrorButtons, // Which buttons to display? buttons: ErrorButtons, // Which buttons to display?
} }
@ -209,27 +214,39 @@ impl ErrorState {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
error: false, error: false,
msg: String::new(), msg: "Unknown Error",
image: RetainedImage::from_image_bytes("banner.png", FERRIS_ERROR).unwrap(), ferris: ErrorFerris::Oops,
buttons: ErrorButtons::Okay, buttons: ErrorButtons::Okay,
} }
} }
// Convenience function to set the [App] error state // Convenience function to set the [App] error state
pub fn set(mut self, new: Self) { pub fn set(&mut self, error: bool, msg: &'static str, ferris: ErrorFerris, buttons: ErrorButtons) {
self = new; *self = Self {
error,
msg,
ferris,
buttons,
};
} }
} }
//---------------------------------------------------------------------------------------------------- [ErrorButtons] enum //---------------------------------------------------------------------------------------------------- [ErrorButtons] enum
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
enum ErrorButtons { pub enum ErrorButtons {
YesNo, YesNo,
YesQuit, StayQuit,
Okay, Okay,
Quit, Quit,
} }
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ErrorFerris {
Oops,
Error,
Panic,
}
//---------------------------------------------------------------------------------------------------- [Images] struct //---------------------------------------------------------------------------------------------------- [Images] struct
struct Images { struct Images {
banner: RetainedImage, banner: RetainedImage,
@ -242,9 +259,9 @@ impl Images {
fn new() -> Self { fn new() -> Self {
Self { Self {
banner: RetainedImage::from_image_bytes("banner.png", BYTES_BANNER).unwrap(), banner: RetainedImage::from_image_bytes("banner.png", BYTES_BANNER).unwrap(),
oops: RetainedImage::from_image_bytes("banner.png", FERRIS_OOPS).unwrap(), oops: RetainedImage::from_image_bytes("oops.png", FERRIS_OOPS).unwrap(),
error: RetainedImage::from_image_bytes("banner.png", FERRIS_ERROR).unwrap(), error: RetainedImage::from_image_bytes("error.png", FERRIS_ERROR).unwrap(),
panic: RetainedImage::from_image_bytes("banner.png", FERRIS_PANIC).unwrap(), panic: RetainedImage::from_image_bytes("panic.png", FERRIS_PANIC).unwrap(),
} }
} }
} }
@ -566,15 +583,23 @@ fn main() {
let mut app = App::new(); let mut app = App::new();
app.now = now; app.now = now;
init_auto(&app); init_auto(&app);
info!("Initialization DONE ... {} seconds", now.elapsed().as_secs_f32()); info!("Init ... DONE ... Took [{}] seconds", now.elapsed().as_secs_f32());
eframe::run_native(&app.name_version.clone(), options, Box::new(|cc| Box::new(App::cc(cc, app))),); eframe::run_native(&app.name_version.clone(), options, Box::new(|cc| Box::new(App::cc(cc, app))),);
} }
impl eframe::App for App { impl eframe::App for App {
// pub fn new() -> Self {
// Self {
// error: false,
// msg: String::new(),
// image: RetainedImage::from_image_bytes("banner.png", FERRIS_ERROR).unwrap(),
// buttons: ErrorButtons::Okay,
// }
// }
fn on_close_event(&mut self) -> bool { fn on_close_event(&mut self) -> bool {
self.quit = true; if self.state.gupax.ask_before_quit {
if self.og.lock().unwrap().gupax.ask_before_quit { self.error_state.set(true, "", ErrorFerris::Oops, ErrorButtons::StayQuit);
self.quit_confirm false
} else { } else {
true true
} }
@ -608,23 +633,49 @@ impl eframe::App for App {
let width = self.width; let width = self.width;
let height = self.height/4.0; let height = self.height/4.0;
ui.style_mut().override_text_style = Some(Name("MonospaceLarge".into())); ui.style_mut().override_text_style = Some(Name("MonospaceLarge".into()));
self.error_state.image.show_max_size(ui, Vec2::new(width, height));
ui.add_sized([width, height], Label::new("--- Gupax has encountered an error ---")); // Display ferris
ui.add_sized([width, height], Label::new(&self.error_state.msg)); use ErrorFerris::*;
let ferris = match self.error_state.ferris {
Oops => &self.img.oops,
Error => &self.img.error,
Panic => &self.img.panic,
};
ferris.show_max_size(ui, Vec2::new(width, height));
// Error/Quit screen
match self.error_state.buttons {
StayQuit => {
let mut text = "--- Are you sure you want to quit? ---".to_string();
if *self.update.lock().unwrap().updating.lock().unwrap() { text = format!("{}\nUpdate is in progress...!", text); }
if self.p2pool { text = format!("{}\nP2Pool is online...!", text); }
if self.xmrig { text = format!("{}\nXMRig is online...!", text); }
ui.add_sized([width, height], Label::new(text))
},
_ => ui.add_sized([width, height], Label::new("--- Gupax has encountered an error! ---")),
};
ui.add_sized([width, height], Label::new(self.error_state.msg));
use ErrorButtons::*; use ErrorButtons::*;
let height = ui.available_height(); let height = ui.available_height();
// Capture [Esc] key
let esc = ctx.input_mut().consume_key(Modifiers::NONE, Key::Escape);
match self.error_state.buttons { match self.error_state.buttons {
YesNo => { YesNo => {
if ui.add_sized([width, height/2.0], egui::Button::new("Yes")).clicked() { self.error_state = ErrorState::new(); } if ui.add_sized([width, height/2.0], egui::Button::new("Yes")).clicked() { self.error_state = ErrorState::new(); }
if ui.add_sized([width, height/2.0], egui::Button::new("No")).clicked() { exit(1); } // If [Esc] was pressed, assume [No]
if esc || ui.add_sized([width, height/2.0], egui::Button::new("No")).clicked() { exit(0); }
}, },
YesQuit => { StayQuit => {
if ui.add_sized([width, height/2.0], egui::Button::new("Okay")).clicked() { self.error_state = ErrorState::new(); } // If [Esc] was pressed, assume [Stay]
if ui.add_sized([width, height/2.0], egui::Button::new("Quit")).clicked() { exit(1); } if esc || ui.add_sized([width, height/2.0], egui::Button::new("Stay")).clicked() {
self.error_state = ErrorState::new();
}
if ui.add_sized([width, height/2.0], egui::Button::new("Quit")).clicked() { exit(0); }
}, },
Okay => if ui.add_sized([width, height], egui::Button::new("Okay")).clicked() { self.error_state = ErrorState::new(); }, Okay => if esc || ui.add_sized([width, height], egui::Button::new("Okay")).clicked() { self.error_state = ErrorState::new(); },
Quit => if ui.add_sized([width, height], egui::Button::new("Quit")).clicked() { exit(1); }, Quit => if ui.add_sized([width, height], egui::Button::new("Quit")).clicked() { exit(0); },
} }
})}); })});
return return
@ -647,80 +698,6 @@ impl eframe::App for App {
} }
drop(og); drop(og);
// Close confirmation.
if self.quit {
// If [ask_before_quit == true]
if self.og.lock().unwrap().gupax.ask_before_quit {
egui::TopBottomPanel::bottom("quit").show(ctx, |ui| {
let width = self.width;
let height = self.height/8.0;
ui.group(|ui| {
if ui.add_sized([width, height], egui::Button::new("Yes")).clicked() {
if self.og.lock().unwrap().gupax.save_before_quit {
if self.diff {
info!("Saving before quit...");
match self.state.save() {
Err(err) => { error!("{}", err); exit(1); },
_ => (),
};
} else {
info!("No changed detected, not saving...");
}
}
info!("Quit confirmation = yes ... goodbye!");
exit(0);
} else if ui.add_sized([width, height], egui::Button::new("No")).clicked() {
self.quit = false;
}
});
});
egui::CentralPanel::default().show(ctx, |ui| {
let width = self.width;
let height = ui.available_height();
let ten = height/10.0;
// Detect processes or update
ui.add_space(ten);
if *self.update.lock().unwrap().updating.lock().unwrap() || self.p2pool || self.xmrig {
ui.add_sized([width, height/4.0], Label::new("Are you sure you want to quit?"));
if *self.update.lock().unwrap().updating.lock().unwrap() { ui.add_sized([width, ten], Label::new("Update is in progress...!")); }
if self.p2pool { ui.add_sized([width, ten], Label::new("P2Pool is online...!")); }
if self.xmrig { ui.add_sized([width, ten], Label::new("XMRig is online...!")); }
// Else, just quit
} else {
if self.og.lock().unwrap().gupax.save_before_quit {
if self.diff {
info!("Saving before quit...");
match self.state.save() {
Err(err) => { error!("{}", err); exit(1); },
_ => (),
};
} else {
info!("No changed detected, not saving...");
}
}
info!("No processes or update in progress ... goodbye!");
exit(0);
}
});
// Else, quit (save if [save_before_quit == true]
} else {
if self.og.lock().unwrap().gupax.save_before_quit {
if self.diff {
info!("Saving before quit...");
match self.state.save() {
Err(err) => { error!("{}", err); exit(1); },
_ => (),
};
} else {
info!("No changed detected, not saving...");
}
}
info!("Quit confirmation = yes ... goodbye!");
exit(0);
}
return
}
// Top: Tabs // Top: Tabs
egui::TopBottomPanel::top("top").show(ctx, |ui| { egui::TopBottomPanel::top("top").show(ctx, |ui| {
let width = (self.width - (SPACE*10.0))/5.0; let width = (self.width - (SPACE*10.0))/5.0;
@ -728,10 +705,11 @@ impl eframe::App for App {
ui.group(|ui| { ui.group(|ui| {
ui.add_space(4.0); ui.add_space(4.0);
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.style_mut().override_text_style = Some(Name("Tab".into())); let style = ui.style_mut();
ui.style_mut().visuals.widgets.inactive.fg_stroke.color = Color32::from_rgb(100, 100, 100); style.override_text_style = Some(Name("Tab".into()));
ui.style_mut().visuals.selection.bg_fill = Color32::from_rgb(255, 120, 120); style.visuals.widgets.inactive.fg_stroke.color = Color32::from_rgb(100, 100, 100);
ui.style_mut().visuals.selection.stroke = Stroke { width: 5.0, color: Color32::from_rgb(255, 255, 255) }; style.visuals.selection.bg_fill = Color32::from_rgb(255, 120, 120);
style.visuals.selection.stroke = Stroke { width: 5.0, color: Color32::from_rgb(255, 255, 255) };
if ui.add_sized([width, height], egui::SelectableLabel::new(self.tab == Tab::About, "About")).clicked() { self.tab = Tab::About; } if ui.add_sized([width, height], egui::SelectableLabel::new(self.tab == Tab::About, "About")).clicked() { self.tab = Tab::About; }
ui.separator(); ui.separator();
if ui.add_sized([width, height], egui::SelectableLabel::new(self.tab == Tab::Status, "Status")).clicked() { self.tab = Tab::Status; } if ui.add_sized([width, height], egui::SelectableLabel::new(self.tab == Tab::Status, "Status")).clicked() { self.tab = Tab::Status; }
@ -868,7 +846,7 @@ impl eframe::App for App {
ui.add_space(10.0); ui.add_space(10.0);
ui.vertical_centered(|ui| { ui.vertical_centered(|ui| {
// Display [Gupax] banner at max, 1/4 the available length // Display [Gupax] banner at max, 1/4 the available length
self.image.banner.show_max_size(ui, Vec2::new(self.width, self.height/4.0)); self.img.banner.show_max_size(ui, Vec2::new(self.width, self.height/4.0));
ui.label("Gupax is a cross-platform GUI for mining"); ui.label("Gupax is a cross-platform GUI for mining");
ui.hyperlink_to("[Monero]", "https://www.github.com/monero-project/monero"); ui.hyperlink_to("[Monero]", "https://www.github.com/monero-project/monero");
ui.label("on the decentralized"); ui.label("on the decentralized");

View file

@ -298,6 +298,7 @@ impl P2pool {
zmq: self.zmq.clone(), zmq: self.zmq.clone(),
}; };
node_vec.push((self.name.clone(), node)); node_vec.push((self.name.clone(), node));
info!("Node | Added [index: {}, name: \"{}\", ip: \"{}\", rpc: {}, zmq: {}]", node_vec_len+1, self.name, self.ip, self.rpc, self.zmq);
} }
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
@ -314,7 +315,7 @@ impl P2pool {
_ => { self.selected_name = node_vec[n-1].0.clone(); self.selected_index = n as u16; }, _ => { self.selected_name = node_vec[n-1].0.clone(); self.selected_index = n as u16; },
}; };
node_vec.remove(n); node_vec.remove(n);
info!("Node | Removed index [{}. {}]", n+1, self.selected_name); info!("Node | Deleted [index: {}, name: \"{}\", ip: \"{}\", rpc: {}, zmq: {}]", n+1, self.selected_name, self.selected_ip, self.selected_rpc, self.selected_zmq);
break break
} }
n += 1; n += 1;