mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-05-06 15:27:28 +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
|
||||
|
||||
[features]
|
||||
default = ["json", "packet_capture"]
|
||||
json = ["dep:serde", "dep:serde_json", "gamedig/serde"]
|
||||
default = ["packet_capture", "bson", "json", "xml", "browser"]
|
||||
|
||||
# Tools
|
||||
packet_capture = ["gamedig/packet_capture"]
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.1.11", features = ["derive"] }
|
||||
gamedig = { version = "*", path = "../lib", features = ["clap"] }
|
||||
thiserror = "1.0.43"
|
||||
# Output formats
|
||||
bson = ["dep:serde", "dep:bson", "dep:hex", "gamedig/serde"]
|
||||
json = ["dep:serde", "dep:serde_json", "gamedig/serde"]
|
||||
xml = ["dep:serde", "dep:serde-xml-rs", "gamedig/serde"]
|
||||
|
||||
# JSON dependencies
|
||||
serde = { version = "1", optional = true }
|
||||
serde_json = { version = "1", optional = true }
|
||||
# Misc
|
||||
browser = ["dep:webbrowser"]
|
||||
|
||||
[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.
|
||||
Copyright (C) 2022 - Present GameDig Organization & Contributors
|
||||
Copyright (C) 2022 - 2023 GameDig Organization & Contributors
|
||||
Licensed under the MIT license
|
||||
";
|
||||
|
||||
|
|
@ -56,10 +56,9 @@ enum Action {
|
|||
#[arg(short, long)]
|
||||
port: Option<u16>,
|
||||
|
||||
/// Flag indicating if the output should be in JSON format for programmatic use.
|
||||
#[cfg(feature = "json")]
|
||||
#[arg(short, long)]
|
||||
json: bool,
|
||||
/// Specifies the output format
|
||||
#[arg(short, long, default_value = "debug", value_enum)]
|
||||
format: OutputFormat,
|
||||
|
||||
/// Which response variant to use when outputting
|
||||
#[arg(short, long, default_value = "generic")]
|
||||
|
|
@ -95,6 +94,19 @@ enum OutputMode {
|
|||
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
|
||||
/// its unique identifier.
|
||||
///
|
||||
|
|
@ -122,13 +134,13 @@ fn find_game(game_id: &str) -> Result<&'static Game> {
|
|||
/// # Returns
|
||||
/// * `Result<IpAddr>` - On sucess returns a resolved IP address; on failure
|
||||
/// returns an [Error::InvalidHostname] error.
|
||||
fn resolve_ip_or_domain(host: &str, extra_options: &mut Option<ExtraRequestSettings>) -> Result<IpAddr> {
|
||||
if let Ok(parsed_ip) = host.parse() {
|
||||
fn resolve_ip_or_domain<T: AsRef<str>>(host: T, extra_options: &mut Option<ExtraRequestSettings>) -> Result<IpAddr> {
|
||||
let host_str = host.as_ref();
|
||||
if let Ok(parsed_ip) = host_str.parse() {
|
||||
Ok(parsed_ip)
|
||||
} else {
|
||||
set_hostname_if_missing(host, extra_options);
|
||||
|
||||
resolve_domain(host)
|
||||
set_hostname_if_missing(host_str, extra_options);
|
||||
resolve_domain(host_str)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,15 +185,36 @@ fn set_hostname_if_missing(host: &str, extra_options: &mut Option<ExtraRequestSe
|
|||
/// # Arguments
|
||||
/// * `args` - A reference to the command line options.
|
||||
/// * `result` - A reference to the result of the query.
|
||||
fn output_result(output: OutputMode, json: bool, result: &dyn CommonResponse) {
|
||||
match output {
|
||||
fn output_result<T: CommonResponse + ?Sized>(output_mode: OutputMode, format: OutputFormat, result: &T) {
|
||||
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")]
|
||||
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")]
|
||||
OutputMode::ProtocolSpecific if json => output_result_json(result.as_original()),
|
||||
|
||||
OutputMode::Generic => output_result_debug(result.as_json()),
|
||||
OutputMode::ProtocolSpecific => output_result_debug(result.as_original()),
|
||||
OutputFormat::Json => match output_mode {
|
||||
OutputMode::Generic => output_result_json(result.as_json()),
|
||||
OutputMode::ProtocolSpecific => output_result_json(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
|
||||
/// * `result` - A serde serializable result.
|
||||
#[cfg(feature = "json")]
|
||||
fn output_result_json<R: serde::Serialize>(result: R) {
|
||||
serde_json::to_writer_pretty(std::io::stdout(), &result).unwrap();
|
||||
fn output_result_json<T: serde::Serialize>(result: T) {
|
||||
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<()> {
|
||||
|
|
@ -210,7 +268,7 @@ fn main() -> Result<()> {
|
|||
game,
|
||||
ip,
|
||||
port,
|
||||
json,
|
||||
format,
|
||||
output_mode,
|
||||
capture,
|
||||
timeout_settings,
|
||||
|
|
@ -225,14 +283,38 @@ fn main() -> Result<()> {
|
|||
gamedig::capture::setup_capture(capture);
|
||||
|
||||
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 => {
|
||||
println!("{}", GAMEDIG_HEADER);
|
||||
|
||||
// Print the source code location
|
||||
println!("Source code: https://github.com/gamedig/rust-gamedig\n");
|
||||
println!("Be sure to leave a star if you like the project :)\n");
|
||||
#[cfg(feature = "browser")]
|
||||
{
|
||||
// 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 => {
|
||||
// Bake the license into the binary
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue