Pretty print option
[hlquery.git] / src / main.rs
1 use std::fmt;
2 use std::fmt::Display;
3 use std::error::Error;
4 use std::net::{SocketAddr, SocketAddrV4, ToSocketAddrs};
5 use clap::Parser;
6 use serde::{Serialize, Serializer};
7 use a2s::A2SClient;
8 use crate::HLQueryError::{IOError,A2SError};
9
10 #[derive(Parser)]
11 #[command(name = "HLQuery")]
12 #[command(author = "MegaBrutal")]
13 #[command(version)]
14 #[command(about = "Query Half-Life servers", long_about = None)]
15 struct Cli {
16 /// Print output in JSON format
17 #[arg(short, long)]
18 json: bool,
19
20 /// Pretty-print JSON or Rust objects
21 #[arg(short, long)]
22 pretty: bool,
23
24 addresses: Vec<String>
25 }
26
27
28 #[derive(Debug, Serialize)]
29 struct HLQueryResult {
30 address: SocketAddrV4,
31 info: Result<a2s::info::Info, HLQueryError>,
32 rules: Result<Vec<a2s::rules::Rule>, HLQueryError>,
33 players: Result<Vec<a2s::players::Player>, HLQueryError>
34 }
35
36 impl HLQueryResult {
37 fn new(a2s_client: &A2SClient, server: SocketAddrV4) -> Self {
38 Self {
39 address: server,
40 info: a2s_client.info(server).map_err(From::from),
41 rules: a2s_client.rules(server).map_err(From::from),
42 players: a2s_client.players(server).map_err(From::from)
43 }
44 }
45 }
46
47 #[derive(Debug, Serialize)]
48 struct HLQuery {
49 input: String,
50 result: Result<Vec<HLQueryResult>, HLQueryError>
51 }
52
53 impl HLQuery {
54 fn new<S: Into<String>>(input: S, result: Result<Vec<HLQueryResult>, HLQueryError>) -> Self {
55 let input = input.into();
56 Self { input, result }
57 }
58 }
59
60 #[derive(Debug)]
61 enum HLQueryError {
62 IOError(std::io::Error),
63 A2SError(a2s::errors::Error)
64 }
65
66 impl Display for HLQueryError {
67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68 match self {
69 IOError(e) => write!(f, "{:?}", e),
70 A2SError(e) => write!(f, "{:?}", e)
71 }
72 }
73 }
74
75 impl Error for HLQueryError {}
76
77 impl Serialize for HLQueryError {
78 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
79 where
80 S: Serializer,
81 {
82 serializer.serialize_str(&format!("{self}"))
83 }
84 }
85
86 impl From<std::io::Error> for HLQueryError {
87 fn from(e: std::io::Error) -> Self {
88 Self::IOError(e)
89 }
90 }
91
92 impl From<a2s::errors::Error> for HLQueryError {
93 fn from(e: a2s::errors::Error) -> Self {
94 Self::A2SError(e)
95 }
96 }
97
98
99 fn main() {
100 let cli = Cli::parse();
101
102 let client = A2SClient::new().unwrap();
103 let query_results: Vec<HLQuery> = cli.addresses.iter()
104 .map(|arg| {
105 let addresses = arg.to_socket_addrs();
106 (arg, addresses)
107 })
108 .map(|lookup_result| match lookup_result {
109 (input, Ok(iter_addr)) => {
110 (input, Ok(iter_addr.filter_map(|sa| match sa {
111 SocketAddr::V4(sa4) => Some(sa4),
112 _ => None
113 })))
114 },
115 (input, Err(e)) => (input, Err(HLQueryError::IOError(e)))
116 })
117 .map(|(input, address_group)| HLQuery::new(input, address_group.map(
118 |addresses| addresses.map(|addr| HLQueryResult::new(&client, addr)).collect())))
119 .collect();
120
121 if cli.json {
122 if cli.pretty {
123 println!("{}", serde_json::to_string_pretty(&query_results).unwrap());
124 }
125 else {
126 println!("{}", serde_json::to_string(&query_results).unwrap());
127 }
128 }
129 else {
130 if cli.pretty {
131 println!("{:#?}", query_results);
132 }
133 else {
134 println!("{:?}", query_results);
135 }
136 }
137 }