Various improvements for the CLI (#159)

* cli: Do DNS lookup if host is not an IP address

* cli: Add option to output as JSON

* cli: Pass hostname to ExtraRequestSettings if it isn't an IP

* cli: Add help docs to all arguments

* cli: Add options for all extra request settings

* cli: Use a CLI only error for DNS

* cli: Add option to set timeout settings

* docs: Update CHANGELOG

* cli: Add default values to TimeoutSettings

* cli: Refactor finding game definition into its own function

Co-Authored-By: Cain <75994858+cainthebest@users.noreply.github.com>

* cli: Refactor IP resolution into its own set of functions

Co-Authored-By: Cain <75994858+cainthebest@users.noreply.github.com>

* cli: Refactor output formatting into its own functions

Co-Authored-By: Cain <75994858+cainthebest@users.noreply.github.com>

* cli: Improve doc comments for CLI args and derive Debug

Co-Authored-By: Cain <75994858+cainthebest@users.noreply.github.com>

* protocols: Derive Serialize for versioned generic responses

This allows for serializing the output of as_original(). We cannot also
derive Deserialize here because the enums use references to the inner
types, which is unavoidable in the current implementation because
as_original() takes a reference to self.

* cli: Add the output mode options

This allows selected whether to use CommonResponse or the original
response struct when outputting.

* cli: Fix ExtraRequestSettings docs showing up in help output

* cli: Add help headings for timeouts and extra request settings

---------

Co-authored-by: Cain <75994858+cainthebest@users.noreply.github.com>
This commit is contained in:
Tom 2023-11-26 22:59:59 +00:00 committed by GitHub
parent b3a29b15b1
commit 7510fe3de0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 209 additions and 13 deletions

View file

@ -55,6 +55,7 @@ impl CommonPlayer for Player {
}
/// Versioned response type
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VersionedResponse<'a> {
Bedrock(&'a BedrockResponse),

View file

@ -17,6 +17,7 @@ pub enum GameSpyVersion {
}
/// Versioned response type
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VersionedResponse<'a> {
One(&'a one::Response),
@ -25,6 +26,7 @@ pub enum VersionedResponse<'a> {
}
/// Versioned player type
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VersionedPlayer<'a> {
One(&'a one::Player),

View file

@ -51,6 +51,7 @@ impl<P: QuakePlayerType> CommonResponse for Response<P> {
}
/// Versioned response type
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VersionedResponse<'a> {
One(&'a Response<crate::protocols::quake::one::Player>),

View file

@ -33,6 +33,7 @@ pub enum Protocol {
}
/// All response types
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub enum GenericResponse<'a> {
GameSpy(gamespy::VersionedResponse<'a>),
@ -50,6 +51,7 @@ pub enum GenericResponse<'a> {
}
/// All player types
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub enum GenericPlayer<'a> {
Valve(&'a valve::ServerPlayer),
@ -149,13 +151,25 @@ pub struct CommonPlayerJson<'a> {
pub score: Option<i32>,
}
#[cfg(feature = "clap")]
fn parse_duration_secs(value: &str) -> Result<Duration, std::num::ParseIntError> {
let secs = value.parse()?;
Ok(Duration::from_secs(secs))
}
/// Timeout settings for socket operations
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "clap", derive(clap::Args))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TimeoutSettings {
#[cfg_attr(feature = "clap", arg(long = "connect-timeout", value_parser = parse_duration_secs, help = "Socket connect timeout (in seconds)", default_value = "4"))]
connect: Option<Duration>,
#[cfg_attr(feature = "clap", arg(long = "read-timeout", value_parser = parse_duration_secs, help = "Socket read timeout (in seconds)", default_value = "4"))]
read: Option<Duration>,
#[cfg_attr(feature = "clap", arg(long = "write-timeout", value_parser = parse_duration_secs, help = "Socket write timeout (in seconds)", default_value = "4"))]
write: Option<Duration>,
/// Number of retries per request
#[cfg_attr(feature = "clap", arg(long, default_value = "0"))]
retries: usize,
}
@ -280,32 +294,38 @@ impl Default for TimeoutSettings {
/// let valve_settings: valve::GatheringSettings = ExtraRequestSettings::default().set_check_app_id(false).into();
/// ```
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "clap", derive(clap::Args))]
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
pub struct ExtraRequestSettings {
/// The server's hostname.
///
/// Used by:
/// - [minecraft::RequestSettings#structfield.hostname]
#[cfg_attr(feature = "clap", arg(long))]
pub hostname: Option<String>,
/// The protocol version to use.
///
/// Used by:
/// - [minecraft::RequestSettings#structfield.protocol_version]
#[cfg_attr(feature = "clap", arg(long))]
pub protocol_version: Option<i32>,
/// Whether to gather player information
///
/// Used by:
/// - [valve::GatheringSettings#structfield.players]
#[cfg_attr(feature = "clap", arg(long))]
pub gather_players: Option<bool>,
/// Whether to gather rule information.
///
/// Used by:
/// - [valve::GatheringSettings#structfield.rules]
#[cfg_attr(feature = "clap", arg(long))]
pub gather_rules: Option<bool>,
/// Whether to check if the App ID is valid.
///
/// Used by:
/// - [valve::GatheringSettings#structfield.check_app_id]
#[cfg_attr(feature = "clap", arg(long))]
pub check_app_id: Option<bool>,
}