mod config;
mod http;
mod peer;
mod udp;
use peer::Peer;
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<Peer>) {
println!("{}", format!("Update interval: {interval}s").yellow());
println!("{}: {seeders}, {}: {leechers}", "S".green(), "L".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()
);
let mut peers = peer::new_buffer(Some(config.peers_buffer_capacity));
match http::request(&query, config.timeout, Some(&mut peers)) {
Ok(response) => stats(
response.interval,
response.leechers,
response.seeders,
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()
);
let mut peers = peer::new_buffer(Some(config.peers_buffer_capacity));
match udp::request(
&config.info_hash,
&address,
config.port,
config.timeout,
Some(&mut peers),
) {
Ok(response) => stats(
response.interval,
response.leechers,
response.seeders,
peers,
),
Err(e) => println!("{}", e.to_string().red()),
}
}
}
Ok(())
}