meowping 2.0.7

A flexible ping utility Tool written in Rust, that is focused on being size efficient and fast.
use crate::colors::{Colorize, HyperLink};
use std::collections::VecDeque;
use std::time::Duration;

pub fn print_with_prefix(minimal: bool, message: &str) {
    if minimal {
        println!("{message}");
    } else {
        println!("{} {}", "[MEOWPING]".magenta(), message);
    }
}

pub fn micros_to_ms(micros: u128) -> f64 {
    Duration::from_micros(u64::try_from(micros).unwrap_or(u64::MAX)).as_secs_f64() * 1000.0
}

pub fn print_statistics(protocol: &str, count: usize, successes: usize, times: &VecDeque<u128>) {
    let failed = count - successes;

    let good_times: Vec<u128> = times.iter().copied().filter(|&t| t > 0).collect();

    let (min_time, max_time, avg_time) = if good_times.is_empty() {
        (0.0, 0.0, 0.0)
    } else {
        let min = good_times.iter().copied().min().map_or(0.0, micros_to_ms);
        let max = good_times.iter().copied().max().map_or(0.0, micros_to_ms);
        let total_ms: f64 = good_times.iter().copied().map(micros_to_ms).sum();
        let sample_count = f64::from(u32::try_from(good_times.len()).unwrap_or(u32::MAX));
        let avg = total_ms / sample_count;
        (min, max, avg)
    };

    let loss_percentage = if count > 0 {
        let failed_count = f64::from(u32::try_from(failed).unwrap_or(u32::MAX));
        let total_count = f64::from(u32::try_from(count).unwrap_or(u32::MAX));
        (failed_count / total_count) * 100.0
    } else {
        0.0
    };

    println!("\n{protocol} Ping statistics:");
    println!(
        "\tAttempted = {}, Successes = {}, Failures = {} ({} loss)",
        count.to_string().bright_blue(),
        successes.to_string().bright_blue(),
        failed.to_string().bright_blue(),
        format!("{loss_percentage:.2}%").bright_blue()
    );
    println!("Approximate round trip times:");
    println!(
        "\tMinimum = {}, Maximum = {}, Average = {}",
        format!("{min_time:.2}ms").bright_blue(),
        format!("{max_time:.2}ms").bright_blue(),
        format!("{avg_time:.2}ms").bright_blue()
    );
}

pub fn color_time(time_ms: f64) -> String {
    let msg = format!("{time_ms:.2}ms");
    match time_ms {
        t if t >= 250.0 => msg.orange(),
        t if t >= 100.0 => msg.yellow(),
        _ => msg.green(),
    }
}

pub fn print_help() {
    let name = env!("CARGO_PKG_NAME");
    let version = env!("CARGO_PKG_VERSION").bright_blue();
    println!(
        "{name} {version} - A flexible ping utility Tool written in Rust, that is focused on being size efficient and fast."
    );
    println!(
        "\n{}: {} <destination> [options]",
        "Usage".bright_blue(),
        name
    );
    println!("\n{}:", "Options".bright_blue());
    println!("    -h, --help                Prints the Help Menu");
    println!(
        "    -p, --port <port(s)>     Port to probe (default: ICMP). Accepts a single port, a comma list (53,80,443), or a range (20-25)"
    );
    println!(
        "    -t, --timeout <timeout>   Set the timeout for each connection attempt in milliseconds (default: 1000ms)"
    );
    println!(
        "    -c, --count <count>       Set the number of connection attempts (default: 65535)"
    );
    println!("    -m, --minimal             Changes the Prints to be more Minimal");
    println!("    -s, --http              Check if the destination URL is online via HTTP/S");
    println!("    -u, --udp              Probe a UDP port instead of using TCP (requires -p)");
    println!("    -a, --no-asn            Disable ASN/organization lookups (use static data)");
    println!(
        "    -C, --config [path]     Load settings from a config file (default: meowping.conf next to the executable)"
    );

    println!("\n{}", "Examples:".bright_blue());

    println!("\n  {}:", "Single Host Ping".yellow());
    println!("    {name} google.com");
    println!("    {name} 8.8.8.8 -c 10");
    println!("    {name} 2606:4700:4700::1111");

    println!("\n  {}:", "TCP Port Check".yellow());
    println!("    {name} example.com -p 443");
    println!("    {name} 192.168.1.1 -p 22 -t 2000");

    println!("\n  {}:", "UDP Port Probe".yellow());
    println!("    {name} 1.1.1.1 -p 53 -u");
    println!("    {name} time.google.com -p 123 -u -c 3");

    println!("\n  {}:", "Multi-Port Probe".yellow());
    println!("    {name} 1.1.1.1 -p 53,80,443");
    println!("    {name} example.com -p 22,80,443,8080");
    println!("    {name} 192.168.1.1 -p 20-25");
    println!("    {name} 1.1.1.1 -p 53,80,443 -u");
    println!("    {name} 192.168.1.0/28 -p 80,443");

    println!("\n  {}:", "HTTP/HTTPS Check".yellow());
    println!("    {name} https://example.com -s");
    println!("    {name} example.com -s -c 5");

    println!("\n  {}:", "Multi-Ping (Multiple Destinations)".yellow());
    println!("    {name} google.com,cloudflare.com,1.1.1.1 -c 2");
    println!("    {name} \"8.8.8.8,1.1.1.1,9.9.9.9\" -c 10");

    println!("\n  {}:", "Subnet Scanning".yellow());
    println!("    {name} 192.168.1.0/24");
    println!("    {name} 10.0.0.0/28 -c 3");
    println!("    {name} 192.168.1.0/24 -p 80");
    println!("    {name} 2001:db8::/120");
    println!("    {name} fe80::/112 -p 22");

    println!("\n{}:", "IPv6 Support".bright_blue());
    println!("    MeowPing supports IPv6 addresses for all connection types (ICMP, TCP, HTTP)");
    println!("    IPv6 subnet scanning is supported up to /112 prefix length");

    println!("\n{}:", "Notes".bright_blue());
    println!("    • Subnet scans default to 1 attempt per host unless -c is specified");
    println!("    • Multi-ping supports mixing hostnames and IP addresses");
    println!("    • ICMP may require elevated privileges on some systems");
    println!(
        "    • UDP probes need no privileges: a response means open, 'Port Unreachable' means closed, and silence is reported as open|filtered"
    );
}

pub fn print_welcome() {
    let version_format = format!("v.{}", env!("CARGO_PKG_VERSION"));
    let name = env!("CARGO_PKG_NAME");
    let hyperlink =
        HyperLink::new(name, "https://github.com/entytaiment25/meowping").expect("valid hyperlink");
    let message = format!(
        "
    /l、
  (゚、 。 7      welcome to {hyperlink}!
    l  ~ヽ       {version_format}
    じしf_,)ノ
"
    )
    .magenta();
    println!("{message}");
}