1 Commits

Author SHA1 Message Date
tmerritt c6dff58d8a Adds colour to the text display
optimized main loop
splits code into structs to better structure
tui shows hosts from the app state
single display shows the stats for a passed TargetState
multi display is woefully incomplete
2025-04-22 16:04:29 -04:00
14 changed files with 506 additions and 538 deletions
Generated
+22 -285
View File
@@ -150,9 +150,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.37"
version = "4.5.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04"
dependencies = [
"clap_builder",
"clap_derive",
@@ -160,9 +160,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.37"
version = "4.5.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5"
dependencies = [
"anstream",
"anstyle",
@@ -243,7 +243,7 @@ checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
dependencies = [
"bitflags",
"crossterm_winapi",
"mio 1.0.3",
"mio",
"parking_lot",
"rustix",
"signal-hook",
@@ -383,95 +383,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "futures"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-executor"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-macro"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "gimli"
version = "0.28.1"
@@ -549,9 +460,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jiff"
version = "0.2.10"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6"
checksum = "1f33145a5cbea837164362c7bd596106eb7c5198f97d1ba6f6ebb3223952e488"
dependencies = [
"jiff-static",
"log",
@@ -562,9 +473,9 @@ dependencies = [
[[package]]
name = "jiff-static"
version = "0.2.10"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254"
checksum = "43ce13c40ec6956157a3635d97a1ee2df323b263f09ea14165131289cb0f5c19"
dependencies = [
"proc-macro2",
"quote",
@@ -629,18 +540,6 @@ dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys 0.48.0",
]
[[package]]
name = "mio"
version = "1.0.3"
@@ -694,7 +593,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"windows-targets 0.52.6",
"windows-targets",
]
[[package]]
@@ -709,25 +608,6 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "ping-rs"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d873f038f84371f9c7fa13f6afea4d5f1fbcd5070ba8eb7af2a6d41c768eff8b"
dependencies = [
"futures",
"mio 0.8.11",
"paste",
"socket2",
"windows",
]
[[package]]
name = "portable-atomic"
version = "1.11.0"
@@ -754,15 +634,14 @@ dependencies = [
"csv",
"env_logger",
"log",
"ping-rs",
"ratatui",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
dependencies = [
"unicode-ident",
]
@@ -924,7 +803,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
dependencies = [
"libc",
"mio 1.0.3",
"mio",
"signal-hook",
]
@@ -937,31 +816,12 @@ dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "socket2"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
@@ -1133,37 +993,13 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
"windows-targets",
]
[[package]]
@@ -1172,22 +1008,7 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
"windows-targets",
]
[[package]]
@@ -1196,64 +1017,28 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@@ -1266,72 +1051,24 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
+2 -2
View File
@@ -15,8 +15,8 @@ color-eyre = "0.6.3"
crossterm = "0.28.1"
ratatui = "0.29.0"
# Internal Ping
ping-rs = "0.1"
[[bin]]
name = "duration_to_string"
[[bin]]
name = "pp"
-1
View File
@@ -1,4 +1,3 @@
address,name
10.3.100.1,Belleville Router
10.11.31.3,Belleville VPN 11-31
10.12.32.1,Belleville VPN 12-32
-1
View File
@@ -1,4 +1,3 @@
address,name
10.3.100.1,Belleville Router
10.11.31.3,Belleville VPN 11-31
10.12.32.1,Belleville VPN 12-32
+6 -7
View File
@@ -1,15 +1,14 @@
10.3.100.1,Belleville Gateway
10.3.100.1,Belleville Router
10.11.31.3,Belleville VPN 11-31
10.12.32.1,Belleville VPN 12-32
192.186.110.6,Belleville Cogeco
129.222.197.36,Belleville Starlink
10.2.100.1,Lindsay Gateway
24.143.184.98,Lindsay Cogeco
192.168.1.50,Lindsay Sign
10.11.21.1,Lindsay VPN 11-21
10.12.22.1,Lindsay VPN 11-22
8.8.8.8,Google DNS
1.1.1.1,1111 DNS
192.168.0.1,Peterborough Gateway
24.51.235.162,Peterborough Cogeco
99.214.0.92,Peterborough Rogers
216.234.202.122,Lindsay Starlink
24.143.184.98,Lindsay Cogeco
192.186.110.6,Belleville Cogeco
129.222.197.36,Belleville Starlink
192.168.0.50,Sign
+41
View File
@@ -0,0 +1,41 @@
use std::time::Duration;
pub fn duration_to_string(to_convert: Duration) -> String {
let mut total_seconds = to_convert.as_secs();
let mut working_string = String::new();
if total_seconds > 86400 {
// days
let num_days = total_seconds / 86400;
working_string = format!("{} days", num_days);
total_seconds = total_seconds - (num_days * 86400);
}
if total_seconds > 3600 {
// hours
let num_hours = total_seconds / 3600;
if num_hours > 0 {
working_string = format!("{} {} hours", working_string, num_hours);
total_seconds = total_seconds - (num_hours * 3600);
}
}
if total_seconds > 60 {
let num_minutes = total_seconds / 60;
if num_minutes > 0 {
working_string = format!("{} {} minutes", working_string, num_minutes);
total_seconds = total_seconds - (num_minutes * 60);
}
// minutes
}
working_string = format!("{} {} seconds.", working_string, total_seconds);
working_string
}
fn main() {
println!("1m 12s => {}", duration_to_string(Duration::from_secs(72)));
println!("1h 1m 12s => {}", duration_to_string(Duration::from_secs(3672)));
println!("1d 1h 1m 12s => {}", duration_to_string(Duration::from_secs(90072)));
println!("30d 1h 1m 12s => {}", duration_to_string(Duration::from_secs(2595672)));
}
+20 -181
View File
@@ -17,209 +17,49 @@ use std::{env, error::Error, ffi::OsString, process};
use color_eyre::owo_colors::OwoColorize;
use crossterm::style::Stylize;
use log::debug;
const SECONDS_IN_MINUTE: u32 = 60;
const SECONDS_IN_HOUR: u32 = SECONDS_IN_MINUTE * 60;
const SECONDS_IN_DAY: u32 = SECONDS_IN_HOUR * 24;
use pp::ppstate::PPState;
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
}
struct PPState {}
impl PPState {
pub fn get_default_targets() -> BTreeMap<String, TargetState> {
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<PathBuf>) -> BTreeMap<String, TargetState> {
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<String, TargetState>) -> Vec<Ipv4Addr> {
let mut working: Vec<Ipv4Addr> = vec![];
for current in to_read_from {
working.push(current.1.target);
}
working
}
/// Simple program to greet a person
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
pub struct AppSettings {
/// Simple program to greet a person
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
pub struct AppSettings {
/// File of list of hosts
#[arg(short, long)]
ping_host_file: Option<PathBuf>,
}
}
fn main() {
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));
print!("Creating new State...");
let mut state = PPState::new_from_file(settings.ping_host_file.unwrap_or(PathBuf::from("INVALID FILE")));
// channel to send requests to ping
let (ping_response_sender, ping_response_listener) = mpsc::channel::<PingResult>();
println!("Setting up requests for {} hosts.", targets.len());
Manager::spawn_manager_thread(ips_from_state(targets.clone()), ping_response_sender.clone());
println!("Setting up requests for {} hosts.", state.ips_from_state().len());
Manager::spawn_manager_thread(PPState::targets_states_from_state(&state), 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()
};
state.check_for_ping(&ping_response_listener);
let now = 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() {
for current_result in state.targets_states_from_state() {
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);
let mut target_string = format!("{} ({})", current_result.name, current_result.target);
while target_string.len() < 34 {
target_string.push(' ');
// target_string = format!("{} ", target_string);
@@ -231,14 +71,13 @@ impl PPState {
target_string.red().to_string()
};
println!("{} \t | {} \t | {}\t | Changed {} ago",
println!("{} \t | {} \t | {}\t | Changed {}s ago",
target_string,
current_result.alive,
current_result.last_rtt,
duration_to_string(time_since_last_change)
current_result.last_rtt, time_since_last_change.as_secs()
);
}
display_loop_start = now;
}
}
}
}
+139
View File
@@ -0,0 +1,139 @@
use pp::tui::target_display::TargetDisplay;
use std::net::Ipv4Addr;
use std::sync::mpsc;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::Sender;
use std::time::Duration;
use color_eyre::Result;
use crossterm::event;
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use pp::ping_result::PingResult;
use pp::target_state::TargetState;
use ratatui::style::Stylize;
use ratatui::text::Line;
use ratatui::widgets::Paragraph;
use ratatui::{DefaultTerminal, Frame};
use std::collections::BTreeMap;
use std::time::SystemTime;
#[derive(Default)]
pub struct App {
running: bool,
counter: u32,
known_hosts: Vec<Ipv4Addr>,
targets: BTreeMap<String, TargetState>,
result_tx: Option<Sender<PingResult>>,
result_rx: Option<Receiver<PingResult>>,
log_events: Vec<String>,
}
impl App {
pub fn new() -> Self {
let mut working = Self::default();
working.known_hosts.push(Ipv4Addr::new(127, 0, 0, 1));
working.known_hosts.push(Ipv4Addr::new(8, 8, 8, 8));
working.known_hosts.push(Ipv4Addr::new(1, 1, 1, 1));
working
.targets
.insert("Test Host 1".to_string(), TargetState {
name: "Test Host 1".to_string(),
target: Ipv4Addr::new(127, 0, 0, 1),
..Default::default()
});
working
.targets
.insert("Test Host 2".to_string(), TargetState {
target: Ipv4Addr::new(1, 1, 1, 1),
name: "Test Host 2".to_string(),
..Default::default()
});
let (sender, receiver) = mpsc::channel::<PingResult>();
working.result_tx = Some(sender);
working.result_rx = Some(receiver);
working
}
pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
self.running = true;
while self.running {
// check if we have any waiting ping responses
if let Ok(response) = self
.result_rx
.as_mut()
.unwrap()
.recv_timeout(Duration::from_millis(50))
{
let local_targets = self.targets.clone();
for (name, current_state) in local_targets {
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: name.clone(),
target: current_state.target,
alive: response.success,
last_rtt: response.rtt,
last_alive_change,
};
self.targets.insert(name.clone(), new_state);
//self.set_target_state(current_state.name.clone(), new_state);
}
}
}
terminal.draw(|frame| self.render(frame))?;
self.handle_crossterm_events()?;
}
Ok(())
}
fn render(&mut self, frame: &mut Frame) {
let title = Line::from("PP Status").bold().centered().blue();
let mut body_string = String::new();
for (name, target) in &self.targets {
frame.render_widget(TargetDisplay::new(target.clone()), frame.area());
body_string = format!("{}\n{} = {}", body_string, target.name, target.target);
}
frame.render_widget(Paragraph::new(body_string), frame.area());
}
fn handle_crossterm_events(&mut self) -> Result<()> {
match event::read()? {
Event::Key(key) if key.kind == KeyEventKind::Press => self.on_key_event(key),
Event::Mouse(_) => {}
Event::Resize(_, _) => {}
_ => {}
}
Ok(())
}
fn on_key_event(&mut self, key: KeyEvent) {
match (key.modifiers, key.code) {
(_, KeyCode::Esc | KeyCode::Char('q'))
| (KeyModifiers::CONTROL, KeyCode::Char('c') | KeyCode::Char('C')) => self.quit(),
_ => {}
}
}
fn quit(&mut self) {
self.running = false;
}
}
fn main() -> Result<()> {
color_eyre::install()?;
let terminal = ratatui::init();
let result = App::new().run(terminal);
ratatui::restore();
println!("Exited.");
result
}
+2 -1
View File
@@ -1,8 +1,9 @@
pub mod manager;
pub mod ping_request;
pub mod ping_result;
pub mod ppstate;
pub mod target_state;
mod tui;
pub mod tui;
pub const SECONDS_BETWEEN_DISPLAY: u32 = 1;
pub const SECONDS_BETWEEN_PING: u32 = 2;
+51 -28
View File
@@ -1,22 +1,30 @@
use std::net::{IpAddr, Ipv4Addr};
use crate::SECONDS_BETWEEN_PING;
use crate::ping_request::PingRequest;
use crate::ping_result::PingResult;
use crate::target_state::TargetState;
use std::net::Ipv4Addr;
use std::process::Command;
use std::sync::mpsc::Sender;
use std::thread;
use std::time::{Duration, SystemTime};
use ping_rs::send_ping;
use crate::ping_request::PingRequest;
use crate::ping_result::PingResult;
use crate::SECONDS_BETWEEN_PING;
pub struct Manager;
const WIN_PING_SUCCESS: i32 = 1;
const LIN_PING_SUCCESS: i32 = 0;
impl Manager {
pub fn spawn_manager_thread(targets: Vec<Ipv4Addr>, sender: Sender<PingResult>) {
pub fn spawn_manager_thread(targets: Vec<TargetState>, sender: Sender<PingResult>) {
let local_targets = targets.clone();
thread::spawn(move || {
loop {
for target in &local_targets {
Manager::spawn_single_ping(PingRequest { target: *target }, sender.clone());
Manager::spawn_single_ping(
PingRequest {
target: target.target,
},
sender.clone(),
);
}
thread::sleep(Duration::from_secs(SECONDS_BETWEEN_PING as u64));
}
@@ -24,38 +32,53 @@ impl Manager {
}
// Spawns a thread that pings a target and sends the response back to the main thread
// via the sender
// via the sender
fn spawn_single_ping(request: PingRequest, sender: Sender<PingResult>) {
let local_request = request.clone();
thread::spawn(move || {
let mut result = 0;
let mut success = true;
let start_time = SystemTime::now();
let target_address = IpAddr::from(request.target);
let options = ping_rs::PingOptions { ttl: 128, dont_fragment: false };
let data = [1,2,3,4];
let result = send_ping(&target_address, Duration::from_secs(10), &data, Some(&options));
#[cfg(any(target_os = "windows"))]
{
result = Command::new("c:/windows/system32/ping.exe")
.arg(request.target.to_string())
.arg("-n 1")
.arg(format!("-w {}", crate::SECONDS_BETWEEN_PING))
.arg("-4")
.output()
.unwrap()
.status
.code()
.unwrap_or(255);
success = result == WIN_PING_SUCCESS as i32;
}
#[cfg(any(target_os = "linux"))]
{
result = Command::new("/usr/bin/ping")
.arg(request.target.to_string())
.arg("-c 1")
.arg("-4")
.arg(format!("-w {}", SECONDS_BETWEEN_PING))
.arg("-W 1")
.output()
.unwrap()
.status
.code()
.unwrap_or(255);
success = result == LIN_PING_SUCCESS as i32;
}
let ping_duration = SystemTime::now()
.duration_since(start_time)
.unwrap_or(Duration::from_secs(600));
let success = match &result {
Ok(reply) => {
println!("Good reply -> {}/{}/{}", reply.address, data.len(), reply.rtt);
true
},
Err(e) => { println!("ERROR {:?}", e); false }
};
sender.send(PingResult {
let success = ping_duration.as_millis() >= SECONDS_BETWEEN_PING as u128 && success;
sender
.send(PingResult {
target: local_request.target,
success,
rtt: ping_duration.as_millis() as u32
}).expect("Unable to send response");
rtt: ping_duration.as_millis() as u32,
})
.expect("Unable to send response");
// println!("Attempt for for {}", local_request.target);
});
+145
View File
@@ -0,0 +1,145 @@
use crate::ping_result::PingResult;
use crate::target_state::TargetState;
use log::debug;
use std::collections::BTreeMap;
use std::fs::File;
use std::net::Ipv4Addr;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::mpsc::Receiver;
use std::time::{Duration, SystemTime};
#[derive(Default)]
pub struct PPState {
targets: BTreeMap<String, TargetState>,
}
impl PPState {
pub fn check_for_ping(&mut self, channel: &Receiver<PingResult>) {
if let Ok(response) = channel.recv_timeout(Duration::from_millis(100)) {
let local_targets = self.targets_states_from_state().clone();
for current_state in local_targets {
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,
};
self.set_target_state(current_state.name.clone(), new_state);
}
}
}
}
pub fn new_from_file(file_to_load_from: PathBuf) -> Self {
let mut working = Self::new();
working.targets = PPState::build_targets_from_file(Some(file_to_load_from));
working
}
pub fn new() -> Self {
Self::default()
}
pub fn get_default_targets() -> BTreeMap<String, TargetState> {
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<PathBuf>) -> BTreeMap<String, TargetState> {
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()
}
}
pub fn ips_from_state(&self) -> Vec<Ipv4Addr> {
let mut working: Vec<Ipv4Addr> = vec![];
for (name, current) in &self.targets {
working.push(current.target);
}
working
}
pub fn targets_states_from_state(&self) -> Vec<TargetState> {
let mut working: Vec<TargetState> = vec![];
for (_, target) in &self.targets {
working.push(target.clone());
}
working
}
pub fn set_target_state(&mut self, key: String, value: TargetState) {
self.targets.insert(key, value);
}
}
+2
View File
@@ -0,0 +1,2 @@
pub mod target_display;
mod multi_target_display;
+15
View File
@@ -0,0 +1,15 @@
use ratatui::buffer::{Buffer, Cell};
use ratatui::layout::Rect;
use ratatui::text::Line;
use ratatui::widgets::{Widget};
pub struct MultiTargetDisplay {
}
impl Widget for MultiTargetDisplay {
fn render(self, area: Rect, buf: &mut Buffer)
{
buf.content.push(Cell::from("Widgets go here"));
}
}
+29
View File
@@ -0,0 +1,29 @@
use crate::target_state::TargetState;
use ratatui::prelude::Buffer;
use ratatui::prelude::Rect;
use ratatui::style::Style;
use ratatui::widgets::Widget;
#[derive(Default)]
pub struct TargetDisplay {
target: TargetState,
}
impl TargetDisplay {
pub fn new(state: TargetState) -> Self {
let mut working = TargetDisplay::default();
working.target = state;
working
}
}
impl Widget for TargetDisplay {
fn render(self, area: Rect, buf: &mut Buffer) {
let row = format!(
"{} -> {} / {} / {}",
self.target.name, self.target.target, self.target.alive, self.target.last_rtt
);
buf.set_string(area.x, area.y, row, Style::default());
}
}