mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-06-01 09:42:41 +00:00
refactor: clean up and add more features
This commit is contained in:
parent
69c2cd8cc9
commit
7ae162bef4
2 changed files with 142 additions and 33 deletions
|
|
@ -11,15 +11,42 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["json", "packet_capture"]
|
default = ["packet_capture", "bson", "json", "xml", "browser"]
|
||||||
json = ["dep:serde", "dep:serde_json", "gamedig/serde"]
|
|
||||||
|
# Tools
|
||||||
packet_capture = ["gamedig/packet_capture"]
|
packet_capture = ["gamedig/packet_capture"]
|
||||||
|
|
||||||
[dependencies]
|
# Output formats
|
||||||
clap = { version = "4.1.11", features = ["derive"] }
|
bson = ["dep:serde", "dep:bson", "dep:hex", "gamedig/serde"]
|
||||||
gamedig = { version = "*", path = "../lib", features = ["clap"] }
|
json = ["dep:serde", "dep:serde_json", "gamedig/serde"]
|
||||||
thiserror = "1.0.43"
|
xml = ["dep:serde", "dep:serde-xml-rs", "gamedig/serde"]
|
||||||
|
|
||||||
# JSON dependencies
|
# Misc
|
||||||
serde = { version = "1", optional = true }
|
browser = ["dep:webbrowser"]
|
||||||
serde_json = { version = "1", optional = true }
|
|
||||||
|
[dependencies]
|
||||||
|
# Core Dependencies
|
||||||
|
thiserror = "1.0.43"
|
||||||
|
clap = { version = "4.1.11", default-features = false, features = ["derive"] }
|
||||||
|
gamedig = { version = "*", path = "../lib", default-features = false, features = [
|
||||||
|
"clap",
|
||||||
|
"games",
|
||||||
|
"game_defs",
|
||||||
|
] }
|
||||||
|
|
||||||
|
# Feature Dependencies
|
||||||
|
# Serialization / Deserialization
|
||||||
|
serde = { version = "1", optional = true, default-features = false }
|
||||||
|
|
||||||
|
# BSON
|
||||||
|
bson = { version = "2.8.1", optional = true, default-features = false }
|
||||||
|
hex = { version = "0.4.3", optional = true, default-features = false }
|
||||||
|
|
||||||
|
# JSON
|
||||||
|
serde_json = { version = "1", optional = true, default-features = false }
|
||||||
|
|
||||||
|
# XML
|
||||||
|
serde-xml-rs = { version = "0.6.0", optional = true, default-features = false }
|
||||||
|
|
||||||
|
# Browser
|
||||||
|
webbrowser = { version = "0.8.12", optional = true, default-features = false }
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ const GAMEDIG_HEADER: &str = r"
|
||||||
|___/
|
|___/
|
||||||
|
|
||||||
A command line interface for querying game servers.
|
A command line interface for querying game servers.
|
||||||
Copyright (C) 2022 - Present GameDig Organization & Contributors
|
Copyright (C) 2022 - 2023 GameDig Organization & Contributors
|
||||||
Licensed under the MIT license
|
Licensed under the MIT license
|
||||||
";
|
";
|
||||||
|
|
||||||
|
|
@ -56,10 +56,9 @@ enum Action {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
port: Option<u16>,
|
port: Option<u16>,
|
||||||
|
|
||||||
/// Flag indicating if the output should be in JSON format for programmatic use.
|
/// Specifies the output format
|
||||||
#[cfg(feature = "json")]
|
#[arg(short, long, default_value = "debug", value_enum)]
|
||||||
#[arg(short, long)]
|
format: OutputFormat,
|
||||||
json: bool,
|
|
||||||
|
|
||||||
/// Which response variant to use when outputting
|
/// Which response variant to use when outputting
|
||||||
#[arg(short, long, default_value = "generic")]
|
#[arg(short, long, default_value = "generic")]
|
||||||
|
|
@ -95,6 +94,19 @@ enum OutputMode {
|
||||||
ProtocolSpecific,
|
ProtocolSpecific,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, ValueEnum)]
|
||||||
|
enum OutputFormat {
|
||||||
|
Debug,
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
JsonPretty,
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
Json,
|
||||||
|
#[cfg(feature = "xml")]
|
||||||
|
Xml,
|
||||||
|
#[cfg(feature = "bson")]
|
||||||
|
Bson,
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempt to find a game from the [library game definitions](GAMES) based on
|
/// Attempt to find a game from the [library game definitions](GAMES) based on
|
||||||
/// its unique identifier.
|
/// its unique identifier.
|
||||||
///
|
///
|
||||||
|
|
@ -122,13 +134,13 @@ fn find_game(game_id: &str) -> Result<&'static Game> {
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// * `Result<IpAddr>` - On sucess returns a resolved IP address; on failure
|
/// * `Result<IpAddr>` - On sucess returns a resolved IP address; on failure
|
||||||
/// returns an [Error::InvalidHostname] error.
|
/// returns an [Error::InvalidHostname] error.
|
||||||
fn resolve_ip_or_domain(host: &str, extra_options: &mut Option<ExtraRequestSettings>) -> Result<IpAddr> {
|
fn resolve_ip_or_domain<T: AsRef<str>>(host: T, extra_options: &mut Option<ExtraRequestSettings>) -> Result<IpAddr> {
|
||||||
if let Ok(parsed_ip) = host.parse() {
|
let host_str = host.as_ref();
|
||||||
|
if let Ok(parsed_ip) = host_str.parse() {
|
||||||
Ok(parsed_ip)
|
Ok(parsed_ip)
|
||||||
} else {
|
} else {
|
||||||
set_hostname_if_missing(host, extra_options);
|
set_hostname_if_missing(host_str, extra_options);
|
||||||
|
resolve_domain(host_str)
|
||||||
resolve_domain(host)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,15 +185,36 @@ fn set_hostname_if_missing(host: &str, extra_options: &mut Option<ExtraRequestSe
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `args` - A reference to the command line options.
|
/// * `args` - A reference to the command line options.
|
||||||
/// * `result` - A reference to the result of the query.
|
/// * `result` - A reference to the result of the query.
|
||||||
fn output_result(output: OutputMode, json: bool, result: &dyn CommonResponse) {
|
fn output_result<T: CommonResponse + ?Sized>(output_mode: OutputMode, format: OutputFormat, result: &T) {
|
||||||
match output {
|
match format {
|
||||||
|
OutputFormat::Debug => match output_mode {
|
||||||
|
OutputMode::Generic => output_result_debug(result.as_json()),
|
||||||
|
OutputMode::ProtocolSpecific => output_result_debug(result.as_original()),
|
||||||
|
},
|
||||||
#[cfg(feature = "json")]
|
#[cfg(feature = "json")]
|
||||||
OutputMode::Generic if json => output_result_json(result.as_json()),
|
OutputFormat::JsonPretty => match output_mode {
|
||||||
|
OutputMode::Generic => output_result_json_pretty(result.as_json()),
|
||||||
|
OutputMode::ProtocolSpecific => output_result_json_pretty(result.as_original()),
|
||||||
|
},
|
||||||
#[cfg(feature = "json")]
|
#[cfg(feature = "json")]
|
||||||
OutputMode::ProtocolSpecific if json => output_result_json(result.as_original()),
|
OutputFormat::Json => match output_mode {
|
||||||
|
OutputMode::Generic => output_result_json(result.as_json()),
|
||||||
OutputMode::Generic => output_result_debug(result.as_json()),
|
OutputMode::ProtocolSpecific => output_result_json(result.as_original()),
|
||||||
OutputMode::ProtocolSpecific => output_result_debug(result.as_original()),
|
},
|
||||||
|
#[cfg(feature = "xml")]
|
||||||
|
OutputFormat::Xml => match output_mode {
|
||||||
|
OutputMode::Generic => output_result_xml(result.as_json()),
|
||||||
|
//BUG: In this case we get a writer error with all serde write methods some reason with xml 0.6.0
|
||||||
|
//BUG: gamedig-cli.exe query -g <GAME> -i <IP> -p <PORT> -f xml --output-mode protocol-specific
|
||||||
|
//BUG: Writer: emitter error: document start event has already been emitted
|
||||||
|
//BUG: With xml 0.5.1 we get unsupported operation: 'serialize_unit_variant'
|
||||||
|
OutputMode::ProtocolSpecific => panic!("XML format is not supported for protocol specific output"),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "bson")]
|
||||||
|
OutputFormat::Bson => match output_mode {
|
||||||
|
OutputMode::Generic => output_result_bson(result.as_json()),
|
||||||
|
OutputMode::ProtocolSpecific => output_result_bson(result.as_original()),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,8 +231,33 @@ fn output_result_debug<R: std::fmt::Debug>(result: R) {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `result` - A serde serializable result.
|
/// * `result` - A serde serializable result.
|
||||||
#[cfg(feature = "json")]
|
#[cfg(feature = "json")]
|
||||||
fn output_result_json<R: serde::Serialize>(result: R) {
|
fn output_result_json<T: serde::Serialize>(result: T) {
|
||||||
serde_json::to_writer_pretty(std::io::stdout(), &result).unwrap();
|
println!("{}", serde_json::to_string(&result).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
fn output_result_json_pretty<T: serde::Serialize>(result: T) {
|
||||||
|
println!("{}", serde_json::to_string_pretty(&result).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Output the result as an XML object.
|
||||||
|
/// # Arguments
|
||||||
|
/// * `result` - A serde serializable result.
|
||||||
|
fn output_result_xml<T: serde::Serialize>(result: T) {
|
||||||
|
println!("{}", serde_xml_rs::to_string(&result).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "bson")]
|
||||||
|
fn output_result_bson<T: serde::Serialize>(result: T) {
|
||||||
|
let bson = bson::to_bson(&result).unwrap();
|
||||||
|
|
||||||
|
if let bson::Bson::Document(document) = bson {
|
||||||
|
let bytes = bson::to_vec(&document).unwrap();
|
||||||
|
|
||||||
|
println!("{}", hex::encode(bytes));
|
||||||
|
} else {
|
||||||
|
panic!("Failed to convert result to BSON");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
|
@ -210,7 +268,7 @@ fn main() -> Result<()> {
|
||||||
game,
|
game,
|
||||||
ip,
|
ip,
|
||||||
port,
|
port,
|
||||||
json,
|
format,
|
||||||
output_mode,
|
output_mode,
|
||||||
capture,
|
capture,
|
||||||
timeout_settings,
|
timeout_settings,
|
||||||
|
|
@ -225,14 +283,38 @@ fn main() -> Result<()> {
|
||||||
gamedig::capture::setup_capture(capture);
|
gamedig::capture::setup_capture(capture);
|
||||||
|
|
||||||
let result = query_with_timeout_and_extra_settings(game, &ip, port, timeout_settings, extra_options)?;
|
let result = query_with_timeout_and_extra_settings(game, &ip, port, timeout_settings, extra_options)?;
|
||||||
output_result(output_mode, json, result.as_ref());
|
output_result(output_mode, format, result.as_ref());
|
||||||
}
|
}
|
||||||
Action::Source => {
|
Action::Source => {
|
||||||
println!("{}", GAMEDIG_HEADER);
|
println!("{}", GAMEDIG_HEADER);
|
||||||
|
|
||||||
// Print the source code location
|
#[cfg(feature = "browser")]
|
||||||
println!("Source code: https://github.com/gamedig/rust-gamedig\n");
|
{
|
||||||
println!("Be sure to leave a star if you like the project :)\n");
|
// Directly offering to open the URL
|
||||||
|
println!("\nWould you like to open the GitHub repository in your default browser? [Y/n]");
|
||||||
|
|
||||||
|
let mut choice = String::new();
|
||||||
|
std::io::stdin().read_line(&mut choice).unwrap();
|
||||||
|
if choice.trim().eq_ignore_ascii_case("Y") {
|
||||||
|
if webbrowser::open("https://github.com/gamedig/rust-gamedig").is_ok() {
|
||||||
|
println!("Opening GitHub repository in default browser...");
|
||||||
|
} else {
|
||||||
|
println!("Failed to open GitHub repository in default browser.");
|
||||||
|
println!("Please use the following URL: https://github.com/gamedig/rust-gamedig");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("Not to worry, you can always open the repository manually");
|
||||||
|
println!("by visiting the following URL: https://github.com/gamedig/rust-gamedig");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "browser"))]
|
||||||
|
{
|
||||||
|
println!("\nYou can find the source code for this project at the following URL:");
|
||||||
|
println!("https://github.com/gamedig/rust-gamedig");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("\nBe sure to leave a star if you like the project :)");
|
||||||
}
|
}
|
||||||
Action::License => {
|
Action::License => {
|
||||||
// Bake the license into the binary
|
// Bake the license into the binary
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue