diff --git a/Cargo.lock b/Cargo.lock index 16b5c20..af76cea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1085,6 +1085,7 @@ dependencies = [ "monero", "num-format", "num_cpus", + "rand", "regex", "reqwest", "serde", diff --git a/src/README.md b/src/README.md index 77d8607..a9d8e87 100644 --- a/src/README.md +++ b/src/README.md @@ -12,8 +12,8 @@ | `main.rs` | Struct/enum/impl for `App/Tab/State`, init functions, main function | `node.rs` | Struct/impl for Community Nodes | `p2pool.rs` | Struct/impl for `P2Pool` tab +| `state.rs` | Struct/impl for `gupax.toml`, the disk state | `status.rs` | Struct/impl for `Status` tab -| `toml.rs` | Struct/impl for `gupax.toml`, the disk state | `xmrig.rs` | Struct/impl for `XMRig` tab ## Bootstrap @@ -42,7 +42,7 @@ This is how Gupax works internally when starting up, divided into 3 sections. - Kill processes, kill connections, exit ## State -Internal state is saved in the "OS data folder" as `gupax.toml`, using the [TOML](https://github.com/toml-lang/toml) format. If the version can't be parsed (not in the `vX.X.X` or `vX.X` format), the auto-updater will be skipped. [If not found, a default `gupax.toml` file will be created with `Toml::default`.](https://github.com/hinto-janaiyo/gupax/blob/main/src/toml.rs) Gupax will `panic!` if `gupax.toml` has IO or parsing issues. +Internal state is saved in the "OS data folder" as `gupax.toml`, using the [TOML](https://github.com/toml-lang/toml) format. If the version can't be parsed (not in the `vX.X.X` or `vX.X` format), the auto-updater will be skipped. [If not found, a default `gupax.toml` file will be created with `State::default`.](https://github.com/hinto-janaiyo/gupax/blob/main/src/state.rs) Gupax will `panic!` if `gupax.toml` has IO or parsing issues. | OS | Data Folder | Example | |----------|----------------------------------------- |-----------------------------------------------------------| diff --git a/src/main.rs b/src/main.rs index 6e516c2..cea0889 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,135 +16,98 @@ // along with this program. If not, see . #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release -use eframe::{egui,NativeOptions}; -use egui::{Vec2,Pos2}; -use std::process::exit; -use std::thread; -use egui::color::Color32; -use egui::Stroke; -use egui::FontId; -use egui::FontFamily::Proportional; -use egui::TextStyle::{Body,Button,Heading,Monospace,Name,Small}; -use egui::RichText; -use egui::Label; -use regex::Regex; -use egui_extras::RetainedImage; -use log::*; -use env_logger::Builder; -use env_logger::WriteStyle; -use std::io::Write; -use std::time::Instant; -use std::sync::{Arc,Mutex}; +//---------------------------------------------------------------------------------------------------- Imports +// egui/eframe +use egui::TextStyle::*; +use egui::color::Color32; +use egui::FontFamily::Proportional; +use egui::{FontId,Label,RichText,Stroke,Vec2,Pos2}; +use egui_extras::RetainedImage; +use eframe::{egui,NativeOptions}; + +// Logging +use log::*; +use env_logger::{Builder,WriteStyle}; + +// std +use std::io::Write; +use std::process::exit; +use std::sync::{Arc,Mutex}; +use std::thread; +use std::time::Instant; + +// Modules mod constants; mod node; -mod toml; +mod state; mod about; mod status; mod gupax; mod p2pool; mod xmrig; -use {constants::*,node::*,crate::toml::*,about::*,status::*,gupax::*,p2pool::*,xmrig::*}; +use {constants::*,node::*,state::*,about::*,status::*,gupax::*,p2pool::*,xmrig::*}; -// The state of the outer [App]. -// See the [State] struct for the -// actual inner state of the settings. +//---------------------------------------------------------------------------------------------------- Struct + Impl +// The state of the outer main [App]. +// See the [State] struct in [state.rs] for the +// actual inner state of the tab settings. 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>, // Are we in the progress of pinging? + node: Arc>, // Data on community nodes + // State: + // og = Old state to compare against + // state = Working state (current settings) + // Instead of comparing [og == state] every frame, + // the [diff] bool will be the signal for [Reset/Save]. + og: State, + state: State, + diff: bool, + // Static stuff + now: Instant, + resolution: Vec2, + os: &'static str, version: String, name_version: String, - tab: Tab, - changed: bool, - os: &'static str, - current_threads: u16, - max_threads: u16, - resolution: Vec2, banner: RetainedImage, + + // TEMPORARY FIXME p2pool: bool, xmrig: bool, - state: State, - og: State, - allowed_to_close: bool, - show_confirmation_dialog: bool, - now: Instant, - ping: bool, - ping_prog: Arc>, - node: Arc>, } impl App { fn new(cc: &eframe::CreationContext<'_>) -> Self { - let version = String::from("v0.0.1"); - let name_version = String::from("Gupax v0.0.1"); - let tab = Tab::default(); - let max_threads = num_cpus::get().try_into().unwrap(); - let current_threads: u16; - let changed = false; - let os = OS; - if max_threads != 1 { - current_threads = max_threads / 2 - } else { - current_threads = 1 - } - let resolution = cc.integration_info.window_info.size; - init_text_styles(&cc.egui_ctx, resolution[0] as f32); - let banner = match RetainedImage::from_image_bytes("banner.png", BYTES_BANNER) { - Ok(banner) => { info!("Banner loading ... OK"); banner }, - Err(err) => { error!("{}", err); panic!("{}", err); }, - }; - let mut state = State::new(); - let mut og = State::new(); - info!("Frame resolution ... {:#?}", resolution); Self { - version, - name_version, - tab, - current_threads, - max_threads, - changed, - resolution, - os, - banner, - p2pool: false, - xmrig: false, - state, - og, - allowed_to_close: false, - show_confirmation_dialog: false, - now: Instant::now(), - node: Arc::new(Mutex::new(NodeStruct::default())), + tab: Tab::default(), + quit: false, + quit_confirm: false, ping: false, ping_prog: Arc::new(Mutex::new(false)), + node: Arc::new(Mutex::new(NodeStruct::default())), + og: State::default(), + state: State::default(), + diff: false, + now: Instant::now(), + resolution: cc.integration_info.window_info.size, + 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, } } } -// Inner state holding all -// mutable tab structs. -#[derive(Clone, Debug, Eq, PartialEq)] -struct State { - gupax: Gupax, - p2pool: P2pool, - xmrig: Xmrig, -} - -impl State { - fn new() -> Self { - Self { - gupax: Gupax::new(), - p2pool: P2pool::new(), - xmrig: Xmrig::new(), - } - } - - fn save(new: State) -> Self { - Self { - gupax: new.gupax, - p2pool: new.p2pool, - xmrig: new.xmrig, - } - } -} - +//---------------------------------------------------------------------------------------------------- Enum + Impl // The tabs inside [App]. #[derive(Clone, Copy, Debug, PartialEq)] enum Tab { @@ -154,12 +117,14 @@ enum Tab { P2pool, Xmrig, } + impl Default for Tab { fn default() -> Self { Self::About } } +//---------------------------------------------------------------------------------------------------- Init functions fn init_text_styles(ctx: &egui::Context, width: f32) { let scale = width / 26.666; let mut style = (*ctx.style()).clone(); @@ -230,23 +195,7 @@ fn init_options() -> NativeOptions { options } -fn main() { - init_logger(); - let toml = match Toml::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 options = init_options(); - eframe::run_native("Gupax", options, Box::new(|cc| Box::new(App::new(cc))),); -} - -// [App] frame for Panic situations. +//---------------------------------------------------------------------------------------------------- [App] frame for [Panic] situations struct Panic { error_msg: String, } impl Panic { fn options() -> NativeOptions { @@ -288,15 +237,35 @@ 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 options = init_options(); + eframe::run_native("Gupax", options, Box::new(|cc| Box::new(App::new(cc))),); +} + impl eframe::App for App { fn on_close_event(&mut self) -> bool { -// self.show_confirmation_dialog = true; - self.ping = true; - self.allowed_to_close + self.quit = true; + self.quit_confirm } + fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { + init_text_styles(ctx, 1280.0); + // Close confirmation. - if self.show_confirmation_dialog { + if self.quit { egui::CentralPanel::default().show(ctx, |ui| { let width = ui.available_width(); let width = width - 10.0; @@ -306,10 +275,10 @@ impl eframe::App for App { ui.group(|ui| { if ui.add_sized([width, height/10.0], egui::Button::new("Yes")).clicked() { info!("Quit confirmation = yes ... goodbye!"); - exit(0); + self.quit_confirm = true; + frame.close(); } else if ui.add_sized([width, height/10.0], egui::Button::new("No")).clicked() { - info!("Quit confirmation = no ... returning!"); - self.show_confirmation_dialog = false; + self.quit = false; } }); }); @@ -329,25 +298,25 @@ impl eframe::App for App { }); } - if *self.ping_prog.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.group(|ui| { - if ui.add_sized([width, height/10.0], egui::Button::new("Yes")).clicked() { - info!("Quit confirmation = yes ... goodbye!"); - exit(0); - } else if ui.add_sized([width, height/10.0], egui::Button::new("No")).clicked() { - info!("Quit confirmation = no ... returning!"); - self.show_confirmation_dialog = false; - } - }); - }); - return - } +// if *self.ping_prog.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.group(|ui| { +// if ui.add_sized([width, height/10.0], egui::Button::new("Yes")).clicked() { +// info!("Quit confirmation = yes ... goodbye!"); +// exit(0); +// } else if ui.add_sized([width, height/10.0], egui::Button::new("No")).clicked() { +// info!("Quit confirmation = no ... returning!"); +// self.show_confirmation_dialog = false; +// } +// }); +// }); +// return +// } // Top: Tabs egui::CentralPanel::default().show(ctx, |ui| { @@ -390,6 +359,9 @@ 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 { @@ -469,7 +441,8 @@ impl eframe::App for App { Status::show(self, ctx, ui); } Tab::Gupax => { - Gupax::show(&mut self.state.gupax, ctx, ui); +// Gupax::show(self.state.gupax, ctx, ui); + exit(0); } Tab::P2pool => { P2pool::show(&mut self.state.p2pool, ctx, ui); diff --git a/src/node.rs b/src/node.rs index c5ea977..9f2145a 100644 --- a/src/node.rs +++ b/src/node.rs @@ -55,12 +55,18 @@ pub struct Data { pub ip: String, } -#[derive(Copy,Clone,Debug,Deserialize,Serialize)] +#[derive(Copy,Clone,Eq,PartialEq,Debug,Deserialize,Serialize)] pub enum NodeEnum { C3pool, Cake, CakeEu, CakeUk, CakeUs, Monerujo, Rino, Selsta, Seth, SupportXmr, SupportXmrIr, XmrVsBeast, } +impl std::fmt::Display for NodeEnum { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:#?}", self) + } +} + #[derive(Debug)] pub struct PingResult { pub node: NodeStruct, @@ -146,7 +152,7 @@ impl NodeStruct { }; let mut timeout = false; let mut mid = Duration::new(0, 0); - let max = rng::thread_rng().gen_range(1..5); + let max = rand::thread_rng().gen_range(1..5); for i in 1..=max { let client = reqwest::blocking::ClientBuilder::new(); let client = reqwest::blocking::ClientBuilder::timeout(client, timeout_sec); @@ -156,7 +162,7 @@ impl NodeStruct { match client.post(http).json(&get_info).send() { Ok(r) => mid += now.elapsed(), Err(err) => { - error!("Timeout on [{}: {}] (over 5 seconds)", id, ip); + error!("Timeout on [{:#?}: {}] (over 5 seconds)", id, ip); mid += timeout_sec; timeout = true; }, diff --git a/src/p2pool.rs b/src/p2pool.rs index 2b84a88..5045249 100644 --- a/src/p2pool.rs +++ b/src/p2pool.rs @@ -16,77 +16,25 @@ // along with this program. If not, see . use crate::App; -use monero::util::address::Address; -use std::str::FromStr; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use crate::constants::*; +use crate::state::P2pool; +use crate::node::NodeEnum; +use crate::node::{RINO,SETH,SELSTA}; -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Node { - Rino, - Seth, - Selsta, -} - -impl Node { - pub fn ip(self) -> String { - match self { - Node::Rino => String::from("node.community.rino.io:18081"), - Node::Seth => String::from("node.sethforprivacy.com:18089"), - Node::Selsta => String::from("selsta1.featherwallet.net:18081"), - } - } - pub fn name(self) -> String { - match self { - Node::Rino => String::from("Rino"), - Node::Seth => String::from("Seth"), - Node::Selsta => String::from("Selsta"), - } - } -} - -// Main data structure for the P2Pool tab -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct P2pool { - pub version: String, - pub sha256: String, - pub manual: bool, - pub mini: bool, - pub rpc: String, - pub zmq: String, +// pub simple: bool, +// pub mini: bool, +// pub out_peers: u8, +// pub in_peers: u8, +// pub log_level: u8, +// pub node: crate::node::NodeEnum, +// pub monerod: String, // pub rpc: u16, // pub zmq: u16, - pub out_peers: u16, - pub in_peers: u16, - pub log: u8, - pub monerod: String, -// pub monerod: std::net::SocketAddr, - pub community: Node, - pub address: String, -// pub address: monero::util::address::Address, -} +// pub address: String, + impl P2pool { - pub fn new() -> Self { - Self { - version: String::from("v2.4"), - sha256: String::from("asdf"), - manual: false, - mini: true, - rpc: String::from(""), - zmq: String::from(""), - out_peers: 10, - in_peers: 10, - log: 3, -// monerod: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 18081), - monerod: String::from(""), - address: String::from(""), - community: Node::Rino, -// address: Address::from_str("44hintoFpuo3ugKfcqJvh5BmrsTRpnTasJmetKC4VXCt6QDtbHVuixdTtsm6Ptp7Y8haXnJ6j8Gj2dra8CKy5ewz7Vi9CYW").unwrap(), - } - } - - pub fn show(state: &mut P2pool, ctx: &egui::Context, ui: &mut egui::Ui) { + pub fn show(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) { let height = ui.available_height() / 10.0; let mut width = ui.available_width() - 50.0; ui.group(|ui| { @@ -100,61 +48,61 @@ impl P2pool { ui.horizontal(|ui| { ui.group(|ui| { ui.vertical(|ui| { ui.group(|ui| { ui.horizontal(|ui| { - if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(state.mini == false, "P2Pool Main")).on_hover_text(P2POOL_MAIN).clicked() { state.mini = false; }; - if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(state.mini == true, "P2Pool Mini")).on_hover_text(P2POOL_MINI).clicked() { state.mini = true; }; + if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(self.mini == false, "P2Pool Main")).on_hover_text(P2POOL_MAIN).clicked() { self.mini = false; }; + if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(self.mini == true, "P2Pool Mini")).on_hover_text(P2POOL_MINI).clicked() { self.mini = true; }; })}); - let width = (width/4.0); + let width = width/4.0; style.spacing.slider_width = width*1.25; ctx.set_style(style); ui.horizontal(|ui| { ui.add_sized([width/8.0, height/5.0], egui::Label::new("Out peers [10-450]:")); - ui.add_sized([width, height/5.0], egui::Slider::new(&mut state.out_peers, 10..=450)).on_hover_text(P2POOL_OUT); + ui.add_sized([width, height/5.0], egui::Slider::new(&mut self.out_peers, 10..=450)).on_hover_text(P2POOL_OUT); }); ui.horizontal(|ui| { ui.add_sized([width/8.0, height/5.0], egui::Label::new(" In peers [10-450]:")); - ui.add_sized([width, height/5.0], egui::Slider::new(&mut state.in_peers, 10..=450)).on_hover_text(P2POOL_IN); + ui.add_sized([width, height/5.0], egui::Slider::new(&mut self.in_peers, 10..=450)).on_hover_text(P2POOL_IN); }); ui.horizontal(|ui| { ui.add_sized([width/8.0, height/5.0], egui::Label::new(" Log level [0-6]:")); - ui.add_sized([width, height/5.0], egui::Slider::new(&mut state.log, 0..=6)).on_hover_text(P2POOL_LOG); + ui.add_sized([width, height/5.0], egui::Slider::new(&mut self.log_level, 0..=6)).on_hover_text(P2POOL_LOG); }); })}); ui.group(|ui| { ui.vertical(|ui| { ui.group(|ui| { ui.horizontal(|ui| { - if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(state.manual == false, "Community Monero Node")).on_hover_text(P2POOL_COMMUNITY).clicked() { state.manual = false; }; - if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(state.manual == true, "Manual Monero Node")).on_hover_text(P2POOL_MANUAL).clicked() { state.manual = true; }; + if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(self.simple == false, "Community Monero Node")).on_hover_text(P2POOL_COMMUNITY).clicked() { self.simple = false; }; + if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(self.simple == true, "Manual Monero Node")).on_hover_text(P2POOL_MANUAL).clicked() { self.simple = true; }; })}); ui.add_space(8.0); ui.horizontal(|ui| { // ui.add_sized([width/8.0, height/5.0], - egui::ComboBox::from_label(Node::name(state.community.clone())).selected_text(Node::ip(state.community.clone())).show_ui(ui, |ui| { - ui.selectable_value(&mut state.community, Node::Rino, Node::ip(Node::Rino)); - ui.selectable_value(&mut state.community, Node::Seth, Node::ip(Node::Seth)); - ui.selectable_value(&mut state.community, Node::Selsta, Node::ip(Node::Selsta)); + egui::ComboBox::from_label(self.node.to_string()).selected_text(RINO).show_ui(ui, |ui| { + ui.selectable_value(&mut self.node, NodeEnum::Rino, RINO); + ui.selectable_value(&mut self.node, NodeEnum::Seth, SETH); + ui.selectable_value(&mut self.node, NodeEnum::Selsta, SELSTA); }); // ); }); - if state.manual == false { ui.set_enabled(false); } + if self.simple == false { ui.set_enabled(false); } let width = (width/4.0); ui.horizontal(|ui| { ui.add_sized([width/8.0, height/7.8], egui::Label::new("Monero Node IP:")); ui.spacing_mut().text_edit_width = ui.available_width() - 35.0; - ui.text_edit_singleline(&mut state.monerod); + ui.text_edit_singleline(&mut self.monerod); }); ui.horizontal(|ui| { ui.add_sized([width/8.0, height/7.8], egui::Label::new("Monero Node RPC Port:")); ui.spacing_mut().text_edit_width = ui.available_width() - 35.0; - ui.text_edit_singleline(&mut state.rpc); + ui.text_edit_singleline(&mut self.rpc.to_string()); }); ui.horizontal(|ui| { ui.add_sized([width/8.0, height/7.8], egui::Label::new("Monero Node ZMQ Port:")); ui.spacing_mut().text_edit_width = ui.available_width() - 35.0; - ui.text_edit_singleline(&mut state.zmq); + ui.text_edit_singleline(&mut self.zmq.to_string()); }); })}); @@ -164,7 +112,7 @@ impl P2pool { ui.horizontal(|ui| { ui.spacing_mut().text_edit_width = ui.available_width(); ui.label("Address:"); - ui.text_edit_singleline(&mut state.address); + ui.text_edit_singleline(&mut self.address); })}); } } diff --git a/src/toml.rs b/src/state.rs similarity index 63% rename from src/toml.rs rename to src/state.rs index ff19174..712f384 100644 --- a/src/toml.rs +++ b/src/state.rs @@ -18,7 +18,7 @@ // This handles reading/parsing the state file: [gupax.toml] // The TOML format is used. This struct hierarchy directly // translates into the TOML parser: -// Toml/ +// State/ // ├─ Gupax/ // │ ├─ ... // ├─ P2pool/ @@ -31,13 +31,12 @@ use std::{fs,env}; use std::fmt::Display; use std::path::{Path,PathBuf}; +use std::result::Result; use serde_derive::{Serialize,Deserialize}; use log::*; //---------------------------------------------------------------------------------------------------- Impl -// Since [State] is already used in [main.rs] to represent -// working state, [Toml] is used to represent disk state. -impl Toml { +impl State { pub fn default() -> Self { use crate::constants::{P2POOL_VERSION,XMRIG_VERSION}; Self { @@ -64,7 +63,9 @@ impl Toml { tls: false, nicehash: false, keepalive: false, - threads: 1, + hugepages_jit: true, + current_threads: 1, + max_threads: 1, priority: 2, pool: "localhost:3333".to_string(), address: "".to_string(), @@ -76,7 +77,7 @@ impl Toml { } } - pub fn get() -> Result { + pub fn get_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 @@ -89,36 +90,67 @@ impl Toml { }, None => { error!("Couldn't get OS PATH for data"); return Err(TomlError::Path(PATH_ERROR.to_string())) }, }; - // Create directory fs::create_dir_all(&path)?; - - // Attempt to read file, create default if not found path.push(FILENAME); - let file = match fs::read_to_string(&path) { - Ok(file) => file, + info!("TOML path ... {}", path.display()); + Ok(path) + } + + // Attempts to read [gupax.toml] or + // attempts to create if not found. + pub fn read_or_create(path: PathBuf) -> Result { + // Attempt to read file, create default if not found + match fs::read_to_string(&path) { + Ok(string) => { + info!("TOML read ... OK"); + Ok(string) + }, Err(err) => { error!("TOML not found, attempting to create default"); - let default = match toml::ser::to_string(&Toml::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)?; + fs::write(&path, &default)?; info!("TOML write ... OK"); - fs::read_to_string(&path)? + Ok(fs::read_to_string(default)?) }, - }; - info!("TOML path ... {}", path.display()); - info!("TOML read ... OK"); + } + } - // Attempt to parse, return Result - match toml::from_str(&file) { + // Attempt to parse from String + pub fn parse(string: String) -> Result { + match toml::de::from_str(&string) { Ok(toml) => { - info!("TOML parse ... OK"); - eprint!("{}", file); + info!("TOML parse ... OK"); + eprint!("{}", string); Ok(toml) }, - Err(err) => { error!("Couldn't parse TOML file"); Err(TomlError::Parse(err)) }, + Err(err) => { error!("Couldn't parse TOML from string"); Err(TomlError::Deserialize(err)) }, + } + } + + // Last three functions combined + // get_path() -> read_or_create() -> parse() +// pub fn get() -> Result { +// let path = Self::path(); +// } + + // Overwrite disk Toml with memory State (save state) + pub fn overwrite(state: State, path: PathBuf) -> Result<(), TomlError> { + info!("Starting TOML overwrite..."); + let string = match toml::ser::to_string(&state) { + Ok(string) => { + info!("TOML parse ... OK"); + eprint!("{}", string); + string + }, + Err(err) => { error!("Couldn't parse TOML into string"); return Err(TomlError::Serialize(err)) }, + }; + match fs::write(&path, string) { + Ok(_) => { info!("TOML overwrite ... OK"); Ok(()) }, + Err(err) => { error!("Couldn't overwrite TOML file"); return Err(TomlError::Io(err)) }, } } } @@ -129,8 +161,8 @@ impl Display for TomlError { match self { Io(err) => write!(f, "{} | {}", ERROR, err), Path(err) => write!(f, "{} | {}", ERROR, err), - Parse(err) => write!(f, "{} | {}", ERROR, err), Serialize(err) => write!(f, "{} | {}", ERROR, err), + Deserialize(err) => write!(f, "{} | {}", ERROR, err), } } } @@ -141,17 +173,10 @@ impl From for TomlError { } } -fn main() { - let state = match Toml::get() { - Ok(state) => { println!("OK"); state }, - Err(err) => panic!(), - }; -} - //---------------------------------------------------------------------------------------------------- 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")] @@ -176,55 +201,57 @@ const DEFAULT_XMRIG_PATH: &'static str = "xmrig/xmrig"; pub enum TomlError { Io(std::io::Error), Path(String), - Parse(toml::de::Error), Serialize(toml::ser::Error), + Deserialize(toml::de::Error), } //---------------------------------------------------------------------------------------------------- Structs -#[derive(Debug,Deserialize,Serialize)] -pub struct Toml { - gupax: Gupax, - p2pool: P2pool, - xmrig: Xmrig, - version: Version, +#[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)] +pub struct State { + pub gupax: Gupax, + pub p2pool: P2pool, + pub xmrig: Xmrig, + pub version: Version, } -#[derive(Debug,Deserialize,Serialize)] -struct Gupax { - auto_update: bool, - ask_before_quit: bool, - p2pool_path: String, - xmrig_path: String, +#[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)] +pub struct Gupax { + pub auto_update: bool, + pub ask_before_quit: bool, + pub p2pool_path: String, + pub xmrig_path: String, } -#[derive(Debug,Deserialize,Serialize)] -struct P2pool { - simple: bool, - mini: bool, - out_peers: u8, - in_peers: u8, - log_level: u8, - node: crate::node::NodeEnum, - monerod: String, - rpc: u16, - zmq: u16, - address: String, +#[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)] +pub struct P2pool { + pub simple: bool, + pub mini: bool, + pub out_peers: u16, + pub in_peers: u16, + pub log_level: u8, + pub node: crate::node::NodeEnum, + pub monerod: String, + pub rpc: u16, + pub zmq: u16, + pub address: String, } -#[derive(Debug,Deserialize,Serialize)] -struct Xmrig { - simple: bool, - tls: bool, - nicehash: bool, - keepalive: bool, - threads: u16, - priority: u8, - pool: String, - address: String, +#[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)] +pub struct Xmrig { + pub simple: bool, + pub tls: bool, + pub nicehash: bool, + pub keepalive: bool, + pub hugepages_jit: bool, + pub max_threads: u16, + pub current_threads: u16, + pub priority: u8, + pub pool: String, + pub address: String, } -#[derive(Debug,Deserialize,Serialize)] -struct Version { - p2pool: String, - xmrig: String, +#[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)] +pub struct Version { + pub p2pool: String, + pub xmrig: String, } diff --git a/src/xmrig.rs b/src/xmrig.rs index 0a3c4d9..f84b072 100644 --- a/src/xmrig.rs +++ b/src/xmrig.rs @@ -20,54 +20,11 @@ use monero::util::address::Address; use std::str::FromStr; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use num_cpus; -use crate::State; use crate::constants::*; - -// Main data structure for the XMRig tab -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Xmrig { - pub version: String, - pub sha256: String, - pub manual: bool, - pub tls: bool, - pub nicehash: bool, - pub keepalive: bool, - pub hugepages_jit: bool, - pub threads: u16, - pub priority: u8, - pub pool: String, - pub address: String, -// pub pool: std::net::SocketAddr, -// pub address: monero::util::address::Address, - pub max_threads: u16, - pub current_threads: u16, -} +use crate::state::Xmrig; impl Xmrig { - pub fn new() -> Self { - let max_threads = num_cpus::get().try_into().unwrap(); - let current_threads: u16; - if max_threads == 1 { current_threads = 1 } else { current_threads = max_threads/2 } - Self { - version: String::from("v6.18.0"), - sha256: String::from("asdf"), - manual: false, - tls: false, - nicehash: false, - keepalive: false, - hugepages_jit: true, - threads: 16, - priority: 2, - pool: String::from(""), - address: String::from(""), -// pool: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 3333), -// address: Address::from_str("44hintoFpuo3ugKfcqJvh5BmrsTRpnTasJmetKC4VXCt6QDtbHVuixdTtsm6Ptp7Y8haXnJ6j8Gj2dra8CKy5ewz7Vi9CYW").unwrap(), - max_threads, - current_threads, - } - } - - pub fn show(state: &mut Xmrig, ctx: &egui::Context, ui: &mut egui::Ui) { + pub fn show(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) { let height = ui.available_height() / 10.0; let mut width = ui.available_width() - 10.0; ui.group(|ui| { @@ -81,18 +38,18 @@ impl Xmrig { ui.horizontal(|ui| { ui.group(|ui| { ui.vertical(|ui| { ui.group(|ui| { ui.horizontal(|ui| { - if ui.add_sized([width/2.0, height/6.0], egui::SelectableLabel::new(state.manual == false, "P2Pool Mode")).on_hover_text(XMRIG_P2POOL).clicked() { state.manual = false; }; - if ui.add_sized([width/2.0, height/6.0], egui::SelectableLabel::new(state.manual == true, "Manual Mode")).on_hover_text(XMRIG_MANUAL).clicked() { state.manual = true; }; + if ui.add_sized([width/2.0, height/6.0], egui::SelectableLabel::new(self.simple == false, "P2Pool Mode")).on_hover_text(XMRIG_P2POOL).clicked() { self.simple = false; }; + if ui.add_sized([width/2.0, height/6.0], egui::SelectableLabel::new(self.simple == true, "Manual Mode")).on_hover_text(XMRIG_MANUAL).clicked() { self.simple = true; }; })}); ui.group(|ui| { ui.horizontal(|ui| { let width = width - 58.0; - ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut state.tls, "TLS Connection")).on_hover_text(XMRIG_TLS); + ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut self.tls, "TLS Connection")).on_hover_text(XMRIG_TLS); ui.separator(); - ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut state.hugepages_jit, "Hugepages JIT")).on_hover_text(XMRIG_HUGEPAGES_JIT); + ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut self.hugepages_jit, "Hugepages JIT")).on_hover_text(XMRIG_HUGEPAGES_JIT); ui.separator(); - ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut state.nicehash, "Nicehash")).on_hover_text(XMRIG_NICEHASH); + ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut self.nicehash, "Nicehash")).on_hover_text(XMRIG_NICEHASH); ui.separator(); - ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut state.keepalive, "Keepalive")).on_hover_text(XMRIG_KEEPALIVE); + ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut self.keepalive, "Keepalive")).on_hover_text(XMRIG_KEEPALIVE); })}); })}); }); @@ -101,28 +58,28 @@ impl Xmrig { style.spacing.slider_width = ui.available_width()/1.25; ctx.set_style(style); ui.horizontal(|ui| { - ui.add_sized([width/8.0, height/8.0], egui::Label::new(format!("Threads [1-{}]:", state.max_threads))); - ui.add_sized([width, height/8.0], egui::Slider::new(&mut state.current_threads, 1..=state.max_threads)).on_hover_text(XMRIG_THREADS); + ui.add_sized([width/8.0, height/8.0], egui::Label::new(format!("Threads [1-{}]:", self.max_threads))); + ui.add_sized([width, height/8.0], egui::Slider::new(&mut self.current_threads, 1..=self.max_threads)).on_hover_text(XMRIG_THREADS); }); ui.horizontal(|ui| { ui.add_sized([width/8.0, height/8.0], egui::Label::new("CPU Priority [0-5]:")); - ui.add_sized([width, height/8.0], egui::Slider::new(&mut state.priority, 0..=5)).on_hover_text(XMRIG_PRIORITY); + ui.add_sized([width, height/8.0], egui::Slider::new(&mut self.priority, 0..=5)).on_hover_text(XMRIG_PRIORITY); }); // }); // ui.group(|ui| { - if state.manual == false { ui.set_enabled(false); } + if self.simple == false { ui.set_enabled(false); } let width = (width/4.0); ui.horizontal(|ui| { ui.add_sized([width/8.0, height/8.0], egui::Label::new("Pool IP:")); ui.spacing_mut().text_edit_width = ui.available_width() - 35.0; - ui.text_edit_singleline(&mut state.pool); + ui.text_edit_singleline(&mut self.pool); }); ui.horizontal(|ui| { ui.add_sized([width/8.0, height/8.0], egui::Label::new("Address:")); ui.spacing_mut().text_edit_width = ui.available_width() - 35.0; - ui.text_edit_singleline(&mut state.address); + ui.text_edit_singleline(&mut self.address); }); // }); }