From: MegaBrutal Date: Mon, 20 Feb 2023 02:15:00 +0000 (+0100) Subject: Implement human-readable output X-Git-Url: http://git.megabrutal.com/?p=hlquery.git;a=commitdiff_plain;h=8a24df94295ec9b8cd42f778ca717ae893a07d5a Implement human-readable output --- diff --git a/src/main.rs b/src/main.rs index 792b64c..6e87bad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,30 @@ use serde::{Serialize, Serializer}; use a2s::A2SClient; use crate::HLQueryError::{IOError,A2SError}; +macro_rules! write_datapoint { + ($formatter:expr,$pad:expr,$caption:expr,$var:expr) => { + writeln!($formatter, "{: { + struct $localtype<'a>(&'a $foreigntype); + + impl Display for $localtype<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.0) + } + } + } +} + +implement_displaydebug!(a2s::info::ServerOS, ServerOS); +implement_displaydebug!(a2s::info::ServerType, ServerType); +implement_displaydebug!(Option, TheShip); +implement_displaydebug!(Option, SourceTVInfo); +implement_displaydebug!(Option, TheShipPlayer); + #[derive(Parser)] #[command(name = "HLQuery")] #[command(author = "MegaBrutal")] @@ -17,6 +41,10 @@ struct Cli { #[arg(short, long)] json: bool, + /// Print output in Rust debug format + #[arg(short, long)] + rust: bool, + /// Pretty-print JSON or Rust objects #[arg(short, long)] pretty: bool, @@ -24,7 +52,6 @@ struct Cli { addresses: Vec } - #[derive(Debug, Serialize)] struct HLQueryResult { address: SocketAddrV4, @@ -44,6 +71,70 @@ impl HLQueryResult { } } +const PAD_WIDTH: usize = 25; + +impl Display for HLQueryResult { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write_datapoint!(f, PAD_WIDTH, "Address:", self.address)?; + match &self.info { + Ok(info) => { + writeln!(f, "\nServer info:")?; + write_datapoint!(f, PAD_WIDTH, "Server name:", info.name)?; + write_datapoint!(f, PAD_WIDTH, "Game:", info.game)?; + write_datapoint!(f, PAD_WIDTH, "Folder:", info.folder)?; + write_datapoint!(f, PAD_WIDTH, "Current map:", info.map)?; + write_datapoint!(f, PAD_WIDTH, "Protocol version:", info.protocol)?; + write_datapoint!(f, PAD_WIDTH, "Steam App ID:", info.app_id)?; + write_datapoint!(f, PAD_WIDTH, "Number of players:", format!("{}/{}", info.players, info.max_players))?; + write_datapoint!(f, PAD_WIDTH, "Number of bots:", info.bots)?; + write_datapoint!(f, PAD_WIDTH, "Server type:", ServerType(&info.server_type))?; + write_datapoint!(f, PAD_WIDTH, "Server OS:", ServerOS(&info.server_os))?; + write_datapoint!(f, PAD_WIDTH, "Password protected:", if info.visibility { "Private" } else { "Public" })?; + write_datapoint!(f, PAD_WIDTH, "VAC enabled:", info.vac)?; + write_datapoint!(f, PAD_WIDTH, "The Ship mode:", TheShip(&info.the_ship))?; + write_datapoint!(f, PAD_WIDTH, "Server version:", info.version)?; + write_datapoint!(f, PAD_WIDTH, "Extra Data Flag:", info.edf)?; + write_datapoint!(f, PAD_WIDTH, "Server port:", info.extended_server_info.port.map_or("Unknown".to_string(), |i| i.to_string()))?; + write_datapoint!(f, PAD_WIDTH, "Steam ID:", info.extended_server_info.steam_id.map_or("Unknown".to_string(), |i| i.to_string()))?; + write_datapoint!(f, PAD_WIDTH, "Keywords:", info.extended_server_info.keywords.clone().unwrap_or("Unknown".to_string()))?; + write_datapoint!(f, PAD_WIDTH, "Game ID:", info.extended_server_info.game_id.map_or("Unknown".to_string(), |i| i.to_string()))?; + write_datapoint!(f, PAD_WIDTH, "Source TV port:", SourceTVInfo(&info.source_tv))?; + }, + Err(e) => + writeln!(f, "Failed to query server info:\t{}", e)? + } + + match &self.rules { + Ok(rules) => { + writeln!(f, "\nGame rules (CVARs):")?; + for rule in rules { + write_datapoint!(f, PAD_WIDTH, rule.name, format!("\"{}\"", rule.value))?; + } + } + Err(e) => + writeln!(f, "Failed to query game rules:\t{}", e)? + } + + match &self.players { + Ok(players) => { + writeln!(f, "\nPlayers:")?; + writeln!(f, "\t{:>3} players online.", players.len())?; + for player in players { + writeln!(f, "{}: {:<30} {:>4} {:>10} {}", + player.index, + player.name, + player.score, + player.duration, + TheShipPlayer(&player.the_ship))?; + } + Ok(()) + } + Err(e) => + writeln!(f, "Failed to query players:\t{}", e) + } + } +} + #[derive(Debug, Serialize)] struct HLQuery { input: String, @@ -57,6 +148,23 @@ impl HLQuery { } } +impl Display for HLQuery { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write_datapoint!(f, PAD_WIDTH, "Input:", self.input)?; + match &self.result { + Ok(v) => { + write_datapoint!(f, PAD_WIDTH, "Number of addresses:", format!("{}\n", v.len()))?; + for r in v { + writeln!(f, "{r}\n")?; + }; + Ok(()) + }, + Err(e) => + writeln!(f, "Failed to execute query:\t{e}\n") + } + } +} + #[derive(Debug)] enum HLQueryError { IOError(std::io::Error), @@ -126,7 +234,7 @@ fn main() { println!("{}", serde_json::to_string(&query_results).unwrap()); } } - else { + else if cli.rust { if cli.pretty { println!("{:#?}", query_results); } @@ -134,4 +242,9 @@ fn main() { println!("{:?}", query_results); } } + else { + for query in query_results { + println!("{}", query); + } + } }