diff --git a/src/console/clients/udp/app.rs b/src/console/clients/udp/app.rs index e365f962..b9e31155 100644 --- a/src/console/clients/udp/app.rs +++ b/src/console/clients/udp/app.rs @@ -64,10 +64,10 @@ use aquatic_udp_protocol::Response::{self, AnnounceIpv4, AnnounceIpv6, Scrape}; use aquatic_udp_protocol::{Port, TransactionId}; use clap::{Parser, Subcommand}; use log::{debug, LevelFilter}; -use serde_json::json; use url::Url; use crate::console::clients::udp::checker; +use crate::console::clients::udp::responses::{AnnounceResponseDto, ScrapeResponseDto}; use crate::shared::bit_torrent::info_hash::InfoHash as TorrustInfoHash; const ASSIGNED_BY_OS: u16 = 0; @@ -117,45 +117,7 @@ pub async fn run() -> anyhow::Result<()> { } => handle_scrape(&tracker_socket_addr, &info_hashes).await?, }; - match response { - AnnounceIpv4(announce) => { - let json = json!({ - "transaction_id": announce.transaction_id.0, - "announce_interval": announce.announce_interval.0, - "leechers": announce.leechers.0, - "seeders": announce.seeders.0, - "peers": announce.peers.iter().map(|peer| format!("{}:{}", peer.ip_address, peer.port.0)).collect::>(), - }); - let pretty_json = serde_json::to_string_pretty(&json).context("announce IPv4 response JSON serialization")?; - println!("{pretty_json}"); - } - AnnounceIpv6(announce) => { - let json = json!({ - "transaction_id": announce.transaction_id.0, - "announce_interval": announce.announce_interval.0, - "leechers": announce.leechers.0, - "seeders": announce.seeders.0, - "peers6": announce.peers.iter().map(|peer| format!("{}:{}", peer.ip_address, peer.port.0)).collect::>(), - }); - let pretty_json = serde_json::to_string_pretty(&json).context("announce IPv6 response JSON serialization")?; - println!("{pretty_json}"); - } - Scrape(scrape) => { - let json = json!({ - "transaction_id": scrape.transaction_id.0, - "torrent_stats": scrape.torrent_stats.iter().map(|torrent_scrape_statistics| json!({ - "seeders": torrent_scrape_statistics.seeders.0, - "completed": torrent_scrape_statistics.completed.0, - "leechers": torrent_scrape_statistics.leechers.0, - })).collect::>(), - }); - let pretty_json = serde_json::to_string_pretty(&json).context("scrape response JSON serialization")?; - println!("{pretty_json}"); - } - _ => println!("{response:#?}"), // todo: serialize to JSON all responses. - }; - - Ok(()) + print_response(response) } fn setup_logging(level: LevelFilter) { @@ -207,6 +169,29 @@ async fn handle_scrape(tracker_socket_addr: &SocketAddr, info_hashes: &[TorrustI .await } +fn print_response(response: Response) -> anyhow::Result<()> { + match response { + AnnounceIpv4(response) => { + let pretty_json = serde_json::to_string_pretty(&AnnounceResponseDto::from(response)) + .context("announce IPv4 response JSON serialization")?; + println!("{pretty_json}"); + } + AnnounceIpv6(response) => { + let pretty_json = serde_json::to_string_pretty(&AnnounceResponseDto::from(response)) + .context("announce IPv6 response JSON serialization")?; + println!("{pretty_json}"); + } + Scrape(response) => { + let pretty_json = + serde_json::to_string_pretty(&ScrapeResponseDto::from(response)).context("scrape response JSON serialization")?; + println!("{pretty_json}"); + } + _ => println!("{response:#?}"), // todo: serialize to JSON all aquatic responses. + }; + + Ok(()) +} + fn parse_socket_addr(tracker_socket_addr_str: &str) -> anyhow::Result { debug!("Tracker socket address: {tracker_socket_addr_str:#?}"); diff --git a/src/console/clients/udp/mod.rs b/src/console/clients/udp/mod.rs index cd0e8bd6..2fcb26ed 100644 --- a/src/console/clients/udp/mod.rs +++ b/src/console/clients/udp/mod.rs @@ -1,2 +1,3 @@ pub mod app; pub mod checker; +pub mod responses; diff --git a/src/console/clients/udp/responses.rs b/src/console/clients/udp/responses.rs new file mode 100644 index 00000000..020c7a36 --- /dev/null +++ b/src/console/clients/udp/responses.rs @@ -0,0 +1,83 @@ +//! Aquatic responses are not serializable. These are the serializable wrappers. +use std::net::{Ipv4Addr, Ipv6Addr}; + +use aquatic_udp_protocol::{AnnounceResponse, ScrapeResponse}; +use serde::Serialize; + +#[derive(Serialize)] +pub struct AnnounceResponseDto { + transaction_id: i32, + announce_interval: i32, + leechers: i32, + seeders: i32, + peers: Vec, +} + +impl From> for AnnounceResponseDto { + fn from(announce: AnnounceResponse) -> Self { + Self { + transaction_id: announce.transaction_id.0, + announce_interval: announce.announce_interval.0, + leechers: announce.leechers.0, + seeders: announce.seeders.0, + peers: announce + .peers + .iter() + .map(|peer| format!("{}:{}", peer.ip_address, peer.port.0)) + .collect::>(), + } + } +} + +impl From> for AnnounceResponseDto { + fn from(announce: AnnounceResponse) -> Self { + Self { + transaction_id: announce.transaction_id.0, + announce_interval: announce.announce_interval.0, + leechers: announce.leechers.0, + seeders: announce.seeders.0, + peers: announce + .peers + .iter() + .map(|peer| format!("{}:{}", peer.ip_address, peer.port.0)) + .collect::>(), + } + } +} + +#[derive(Serialize)] +pub struct ScrapeResponseDto { + transaction_id: i32, + torrent_stats: Vec, +} + +impl From for ScrapeResponseDto { + fn from(scrape: ScrapeResponse) -> Self { + Self { + transaction_id: scrape.transaction_id.0, + torrent_stats: scrape + .torrent_stats + .iter() + .map(|torrent_scrape_statistics| TorrentStats { + seeders: torrent_scrape_statistics.seeders.0, + completed: torrent_scrape_statistics.completed.0, + leechers: torrent_scrape_statistics.leechers.0, + }) + .collect::>(), + } + } +} + +#[derive(Serialize)] +struct Peer { + seeders: i32, + completed: i32, + leechers: i32, +} + +#[derive(Serialize)] +struct TorrentStats { + seeders: i32, + completed: i32, + leechers: i32, +}