diff --git a/Cargo.lock b/Cargo.lock index 64064d1..871a402 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -709,7 +709,7 @@ dependencies = [ [[package]] name = "pp" -version = "0.2.0" +version = "0.2.1-PREVIEW" dependencies = [ "ansi_term", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 9715458..a9ae742 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pp" -version = "0.2.0" +version = "0.2.1-PREVIEW" edition = "2024" [dependencies] diff --git a/src/app_settings.rs b/src/app_settings.rs index fb55ffe..4c1f5c6 100644 --- a/src/app_settings.rs +++ b/src/app_settings.rs @@ -5,6 +5,6 @@ use clap::Parser; #[command(version, about, long_about = None)] pub struct AppSettings { /// File of list of hosts - #[arg(short, long)] + #[arg(short, long, default_value = None)] pub ping_host_file: Option, -} \ No newline at end of file +} diff --git a/src/bin/pp.rs b/src/bin/pp.rs index 1970104..2082eab 100644 --- a/src/bin/pp.rs +++ b/src/bin/pp.rs @@ -1,240 +1,14 @@ -use std::collections::BTreeMap; -use std::fs::File; -use std::io; -use std::io::BufRead; -use std::net::Ipv4Addr; -use std::path::{Path, PathBuf}; -use std::str::FromStr; -use std::sync::mpsc; -use std::time::{Duration, SystemTime}; use clap::Parser; -use pp::ping_result::PingResult; -use pp::ping_request::PingRequest; -use pp::manager::Manager; -use pp::SECONDS_BETWEEN_DISPLAY; -use pp::target_state::TargetState; -use std::{env, error::Error, ffi::OsString, process}; -use color_eyre::owo_colors::OwoColorize; -use crossterm::style::Stylize; -use log::debug; +use ratatui::widgets::TableState; use pp::app_settings::AppSettings; +use pp::tui::ratatui_app::RatatuiApp; +fn main() -> color_eyre::Result<()> { + // find out what file we are using to get our hosts + let settings = AppSettings::parse(); -const SECONDS_IN_MINUTE: u32 = 60; -const SECONDS_IN_HOUR: u32 = SECONDS_IN_MINUTE * 60; -const SECONDS_IN_DAY: u32 = SECONDS_IN_HOUR * 24; - -pub fn duration_to_string(to_convert: Duration) -> String { - let mut total_seconds = to_convert.as_secs() as u32; - let mut working_string = String::new(); - - if total_seconds > 86400 { - // days - let num_days = (total_seconds / SECONDS_IN_DAY) as u32; - working_string = format!("{} days", num_days); - total_seconds = total_seconds - (num_days * SECONDS_IN_DAY); - } - - if total_seconds > 3600 { - // hours - let num_hours = (total_seconds / SECONDS_IN_HOUR) as u32; - if num_hours > 0 { - working_string = format!("{} {} hours", working_string, num_hours); - total_seconds = total_seconds - (num_hours * SECONDS_IN_HOUR); - } - } - if total_seconds > 60 { - let num_minutes = (total_seconds / SECONDS_IN_MINUTE) as u32; - if num_minutes > 0 { - working_string = format!("{} {} minutes", working_string, num_minutes); - total_seconds = total_seconds - (num_minutes * SECONDS_IN_MINUTE); - } - // minutes - } - - - working_string = format!("{} {} seconds", working_string, total_seconds); - - working_string + color_eyre::install()?; + let terminal = ratatui::init(); + let result = RatatuiApp::new(settings.ping_host_file).run(terminal); + ratatui::restore(); + result } - - -struct PPState {} - -impl PPState { - pub fn get_default_targets() -> BTreeMap { - let mut working = BTreeMap::new(); - working.insert("Localhost".to_string(), - TargetState { - name: "Localhost".to_string(), - target: Ipv4Addr::new(127, 0, 0, 1), - ..TargetState::default() - }, - ); - - working.insert("Home Gateway".to_string(), - TargetState { - name: "Home Gateway".to_string(), - target: Ipv4Addr::new(172, 24, 0, 1), - ..TargetState::default() - }, - ); - - working.insert("1111 DNS".to_string(), - TargetState { - name: "1111 DNS".to_string(), - target: Ipv4Addr::new(1, 1, 1, 1), - ..TargetState::default() - }, - ); - working.insert("Google DNS".to_string(), - TargetState { - name: "Google DNS".to_string(), - target: Ipv4Addr::new(8, 8, 8, 8), - ..TargetState::default() - }, - ); - - working.insert("Site IP 1".to_string(), - TargetState { - name: "Site IP 1".to_string(), - target: Ipv4Addr::new(216, 121, 247, 231), - ..TargetState::default() - }, - ); - working.insert("Site IP 2".to_string(), - TargetState { - name: "Site IP 2".to_string(), - target: Ipv4Addr::new(216, 234, 202, 122), - ..TargetState::default() - }, - ); - working.insert("Site IP 3".to_string(), - TargetState { - name: "Site IP 3".to_string(), - target: Ipv4Addr::new(24, 143, 184, 98), - ..TargetState::default() - }, - ); - working - } - - pub fn build_targets_from_file(filename: Option) -> BTreeMap { - // PPState::get_default_targets(); - if let Some(file) = filename { - let mut working = BTreeMap::new(); - if !&file.exists() { - debug!("Cant load hosts from {:?}. Using default host list.", file.clone().as_os_str()); - // use - PPState::get_default_targets() - } else { - debug!("LOADING HOSTS FROM {:?}", file.to_str()); - let file = File::open(file); - let mut rdr = csv::Reader::from_reader(file.unwrap()); - for result in rdr.records() { - let record = result.unwrap(); - working.insert(record[1].to_string(), - TargetState { - name: record[1].to_string(), - target: Ipv4Addr::from_str(&record[0]).unwrap(), - alive: false, - last_alive_change: SystemTime::now(), - last_rtt: 0, - }); - } - working - } - } else { - PPState::get_default_targets() - } - } -} - fn ips_from_state(to_read_from: BTreeMap) -> Vec { - let mut working: Vec = vec![]; - for current in to_read_from { - working.push(current.1.target); - } - working - } - - /// Simple program to greet a person - - - fn main() { - // Get App Settings - let settings = AppSettings::parse(); - - print!("Prep to load targets..."); - let file_to_check = match settings.ping_host_file { - None => { - PathBuf::from("./hosts.txt") - } - Some(actual) => { - actual - } - }; - let mut targets = PPState::build_targets_from_file(Some(file_to_check)); - - // channel to send requests to ping - let (ping_response_sender, ping_response_listener) = mpsc::channel::(); - - println!("Setting up requests for {} hosts.", targets.len()); - Manager::spawn_manager_thread(ips_from_state(targets.clone()), ping_response_sender.clone()); - - let mut display_loop_start = SystemTime::now(); - let mut duration_since_last_loop = SystemTime::now().duration_since(display_loop_start).unwrap(); - loop { - let now = SystemTime::now(); - if let Ok(response) = ping_response_listener.recv_timeout(Duration::from_millis(100)) { - let local_targets = targets.clone(); - for (_, (name, current_state)) in local_targets.iter().enumerate() { - if current_state.target == response.target { - let last_alive_change = if response.success == current_state.alive { - current_state.last_alive_change - } else { - SystemTime::now() - }; - - let new_state = TargetState { - name: current_state.name.clone(), - target: current_state.target, - alive: response.success, - last_rtt: response.rtt, - last_alive_change, - }; - targets.insert(name.clone(), new_state); - } - } - } - duration_since_last_loop = now - .duration_since(display_loop_start) - .expect("unable to figure out how long ago we displayed stuff"); - if duration_since_last_loop.as_secs() > SECONDS_BETWEEN_DISPLAY as u64 { - println!("DISPLAY LOOP"); - println!("Host \t\t\t\t\t | Alive \t | RTT \t\t"); - for (name, current_result) in targets.clone() { - let time_since_last_change = now - .duration_since(current_result.last_alive_change) - .unwrap_or(Duration::from_secs(0)); - let mut target_string = format!("{} ({})", name, current_result.target); - while target_string.len() < 34 { - target_string.push(' '); - // target_string = format!("{} ", target_string); - } - - target_string = if current_result.alive { - target_string.green().to_string() - } else { - target_string.red().to_string() - }; - - println!("{} \t | {} \t | {}\t | Changed {} ago", - target_string, - current_result.alive, - current_result.last_rtt, - duration_to_string(time_since_last_change) - ); - } - display_loop_start = now; - } - } - } diff --git a/src/bin/rat.rs b/src/bin/rat.rs deleted file mode 100644 index 7b3f576..0000000 --- a/src/bin/rat.rs +++ /dev/null @@ -1,15 +0,0 @@ -use clap::Parser; -use ratatui::widgets::TableState; -use pp::app_settings::AppSettings; -use pp::tui::ratatui_app::RatatuiApp; -fn main() -> color_eyre::Result<()> { - // find out what file we are using to get our hosts - let settings = AppSettings::parse(); - - color_eyre::install()?; - let terminal = ratatui::init(); - let result = RatatuiApp::new(settings.ping_host_file).run(terminal); - ratatui::restore(); - result -} - diff --git a/src/tui/mode_deleting.rs b/src/tui/mode_deleting.rs index 6f84c4c..6e10cfa 100644 --- a/src/tui/mode_deleting.rs +++ b/src/tui/mode_deleting.rs @@ -29,7 +29,7 @@ impl RatatuiDeletingMode { match event::read()? { Event::Key(key) if key.kind == KeyEventKind::Press => match key.code { KeyCode::Enter | KeyCode::Char('y') | KeyCode::Char('Y') => { - println!("TiME TO DELETE SELECTED"); + } KeyCode::Esc | KeyCode::Char('n') | KeyCode::Char('N') => { app.set_screen(Monitoring) diff --git a/src/tui/mode_monitoring.rs b/src/tui/mode_monitoring.rs index a03156c..dfd2f3f 100644 --- a/src/tui/mode_monitoring.rs +++ b/src/tui/mode_monitoring.rs @@ -7,7 +7,7 @@ use ratatui::widgets::{Block, Borders, Cell, List, ListItem, ListState, Paragrap use ratatui::Frame; use std::time::{Duration, SystemTime}; use crate::tui::ratatui_app::RatatuiApp; -use crate::tui::ratatui_screens::RatatuiScreens::Exiting; +use crate::tui::ratatui_screens::RatatuiScreens::{Deleting, Editing, Exiting}; pub struct RatatuiMonitoringMode {} @@ -80,7 +80,7 @@ impl RatatuiMonitoringMode { let table = Table::new(rows, vec![Constraint::Min(30), Constraint::Min(6), Constraint::Min(5), Constraint::Min(30)]) .header(header) .block(Block::default() - .title("Hosts") + .title(Line::from(format!("PP v{}", env!("CARGO_PKG_VERSION")))) .borders(Borders::ALL)) .widths(&[ Constraint::Min(30), @@ -99,8 +99,8 @@ impl RatatuiMonitoringMode { // frame.render_widget(table, layouts[0]); frame.render_stateful_widget(table, layouts[0], &mut make_state(state.selected_host)); - let footer_text = "Press or q to exit"; -// let footer_text = "Press or q to exit | Press d to delete host | Press a to add host"; + // let footer_text = "Press or q to exit"; + let footer_text = format!("Press or q to exit | Press d to delete host | Press a to add host - ({:?})", state.filename); let mut list_items = vec![]; for entry in state.get_log_entries(10) { @@ -151,12 +151,12 @@ impl RatatuiMonitoringMode { (_, KeyCode::Esc) | (_, KeyCode::Char('q')) | (_, KeyCode::Char('Q')) => { app.set_screen(Exiting); } - // (_, KeyCode::Char('e')) | (_, KeyCode::Char('E')) => { - // app.set_screen(Editing); - // } - // (_, KeyCode::Char('d')) | (_, KeyCode::Char('D')) => { - // app.set_screen(Deleting); - // } + (_, KeyCode::Char('e')) | (_, KeyCode::Char('E')) => { + app.set_screen(Editing); + } + (_, KeyCode::Char('d')) | (_, KeyCode::Char('D')) => { + app.set_screen(Deleting); + } _ => {} } } diff --git a/src/tui/ratatui_app.rs b/src/tui/ratatui_app.rs index 3b70932..a5bb451 100644 --- a/src/tui/ratatui_app.rs +++ b/src/tui/ratatui_app.rs @@ -13,7 +13,9 @@ use std::path::PathBuf; use std::str::FromStr; use std::sync::mpsc; use std::sync::mpsc::Receiver; +use std::thread; use std::time::{Duration, SystemTime}; +use log::debug; use crate::tui::mode_adding::RatatuiAddingMode; use crate::tui::mode_deleting::RatatuiDeletingMode; use crate::tui::mode_editing::RatatuiEditingMode; @@ -28,7 +30,7 @@ pub struct RatatuiApp { pub state: BTreeMap, current_screen: RatatuiScreens, log_entries: Vec, - filename: Option, + pub(crate) filename: Option, pub selected_host: usize } @@ -163,34 +165,62 @@ impl RatatuiApp { /// Public Methods impl RatatuiApp { - pub fn new(option: Option) -> Self { + fn load_hosts_from_file(file_to_load_from: PathBuf) -> BTreeMap { + let mut working = BTreeMap::new(); + + let the_file = File::open(file_to_load_from.clone()); + let mut rdr = csv::Reader::from_reader(the_file.unwrap()); + for result in rdr.records() { + let record = result.unwrap(); + working.insert(record[1].to_string(), TargetState { + name: record[1].to_string(), + target: Ipv4Addr::from_str(&record[0]).unwrap(), + alive: false, + last_alive_change: SystemTime::now(), + last_rtt: 0 + }); + } + + working + } + + pub fn new(option: Option) -> Self { let mut working = Self::default(); let targets = if let Some(file) = option { - let mut working_btree = BTreeMap::new(); - if !&file.exists() { - RatatuiApp::get_default_targets(); - working.filename = Some("hosts.txt".to_string()); - } else { - let real_file = File::open(file.clone()); + if file.exists() { working.filename = Some(file.as_os_str().to_string_lossy().parse().unwrap()); - let mut rdr = csv::Reader::from_reader(real_file.unwrap()); - for result in rdr.records() { - let record = result.unwrap(); - working_btree.insert(record[1].to_string(), TargetState { - name: record[1].to_string(), - target: Ipv4Addr::from_str(&record[0]).unwrap(), - alive: false, - last_alive_change: SystemTime::now(), - last_rtt: 0, - }); + } else { + // working.log_event("Passed file doesnt exist looking for hosts.txt".to_string()); + if !&PathBuf::from_str("hosts.txt").unwrap().exists() { + // working.log_event("Didnt find hosts.txt".to_string()); + working.filename = None; + } else { + // working.log_event("Found hosts.txt. using it as the default".to_string()); + working.filename = Some("hosts.txt".to_string()); } } - working_btree + + if let Some(file) = working.filename.clone() { + RatatuiApp::load_hosts_from_file(PathBuf::from_str(&*working.filename.clone().unwrap()).unwrap()) + } else { + RatatuiApp::get_default_targets() + } } else { - RatatuiApp::get_default_targets() + // none was passed for our parameter. + working.filename = Some("hosts.txt".to_string()); + if let Some(ref hosts_file) = working.filename { + if PathBuf::from_str(hosts_file).unwrap().exists() { + RatatuiApp::load_hosts_from_file(PathBuf::from_str(&*hosts_file.clone()).unwrap()) + } else { + working.filename = None; + RatatuiApp::get_default_targets() + } + } else { + RatatuiApp::get_default_targets() + } }; - working.state = targets; + working.state = targets.clone(); working }