btpeer 0.4.0

Simple CLI tool and library to get peers from TCP/HTTP and UDP BitTorrent trackers
Documentation
mod config;
mod http;
mod udp;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    use clap::Parser;
    use colored::Colorize;
    use config::Config;

    fn stats(
        interval: u32,
        leechers: u32,
        seeders: u32,
        peers: Vec<cyphernet::addr::PartialAddr<cyphernet::addr::HostName, 0>>,
    ) {
        println!("{}", format!("Update interval: {interval}s").yellow());
        println!("{}: {leechers}, {}: {seeders}", "L".green(), "S".red());
        if peers.is_empty() {
            println!("{}", "No peers.".red());
        } else {
            for peer in peers {
                println!("{peer}")
            }
        }
    }

    let config = Config::parse();

    if config.http_tracker.as_ref().is_none_or(|h| h.is_empty())
        && config.udp_tracker.as_ref().is_none_or(|u| u.is_empty())
    {
        println!("{}", "Provide at least one HTTP or UDP tracker(s)".red());
        std::process::exit(1);
    }

    if let Some(http_tracker) = config.http_tracker {
        for url in http_tracker {
            let query = http::Query::new(url.as_str(), &config.info_hash, config.port)?;
            println!(
                "{}",
                format!("Sending HTTP announce to `{query}`...").blue()
            );
            match http::request(&query, config.timeout) {
                Ok(response) => stats(
                    response.interval,
                    response.leechers,
                    response.seeders,
                    response.peers,
                ),
                Err(e) => println!("{}", e.to_string().red()),
            }
        }
    }

    if let Some(udp_tracker) = config.udp_tracker {
        for address in udp_tracker {
            println!(
                "{}",
                format!("Sending UDP announce to `{address}`...").blue()
            );
            match udp::request(&config.info_hash, &address, config.port, config.timeout) {
                Ok(response) => stats(
                    response.interval,
                    response.leechers,
                    response.seeders,
                    response.peers,
                ),
                Err(e) => println!("{}", e.to_string().red()),
            }
        }
    }

    Ok(())
}