diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d09f966..3627fe4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,10 @@ Game: Protocols: - Valve: Removed `SteamApp` due to it not being really useful at all, replaced all instances with `Engine`. +Crate: +- TimeoutSettings should now be imported from the root of the crate + - `use gamedig::TimeoutSettings` + # 0.4.1 - 13/10/2023 ### Changes: Game: diff --git a/crates/lib/examples/generic.rs b/crates/lib/examples/generic.rs index 0365dbfa..4d08c4d4 100644 --- a/crates/lib/examples/generic.rs +++ b/crates/lib/examples/generic.rs @@ -1,8 +1,9 @@ use gamedig::{ - protocols::types::{CommonResponse, ExtraRequestSettings, TimeoutSettings}, + protocols::types::{CommonResponse, ExtraRequestSettings}, query_with_timeout_and_extra_settings, GDResult, Game, + TimeoutSettings, GAMES, }; @@ -77,7 +78,7 @@ fn main() { #[cfg(test)] mod test { - use gamedig::{protocols::types::TimeoutSettings, GAMES}; + use gamedig::{TimeoutSettings, GAMES}; use std::{ net::{IpAddr, Ipv4Addr}, time::Duration, diff --git a/crates/lib/examples/valve_protocol_query.rs b/crates/lib/examples/valve_protocol_query.rs index 5e9de3f2..279cc0e5 100644 --- a/crates/lib/examples/valve_protocol_query.rs +++ b/crates/lib/examples/valve_protocol_query.rs @@ -1,6 +1,6 @@ -use gamedig::protocols::types::TimeoutSettings; use gamedig::protocols::valve; use gamedig::protocols::valve::{Engine, GatheringSettings}; +use gamedig::TimeoutSettings; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::time::Duration; diff --git a/crates/lib/src/games/ffow.rs b/crates/lib/src/games/ffow.rs index 9cbbdfc8..61d2907e 100644 --- a/crates/lib/src/games/ffow.rs +++ b/crates/lib/src/games/ffow.rs @@ -1,8 +1,8 @@ use crate::buffer::{Buffer, Utf8Decoder}; -use crate::protocols::types::{CommonResponse, TimeoutSettings}; +use crate::protocols::types::CommonResponse; use crate::protocols::valve::{Engine, Environment, Server, ValveProtocol}; use crate::protocols::GenericResponse; -use crate::GDResult; +use crate::{GDResult, TimeoutSettings}; use byteorder::LittleEndian; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; diff --git a/crates/lib/src/games/jc2m.rs b/crates/lib/src/games/jc2m.rs index 3ad22b24..405c848c 100644 --- a/crates/lib/src/games/jc2m.rs +++ b/crates/lib/src/games/jc2m.rs @@ -1,10 +1,10 @@ use crate::buffer::{Buffer, Utf8Decoder}; use crate::protocols::gamespy::common::has_password; use crate::protocols::gamespy::three::{data_to_map, GameSpy3}; -use crate::protocols::types::{CommonPlayer, CommonResponse, GenericPlayer, TimeoutSettings}; +use crate::protocols::types::{CommonPlayer, CommonResponse, GenericPlayer}; use crate::protocols::GenericResponse; use crate::GDErrorKind::{PacketBad, TypeParse}; -use crate::{GDErrorKind, GDResult}; +use crate::{GDErrorKind, GDResult, TimeoutSettings}; use byteorder::BigEndian; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; diff --git a/crates/lib/src/games/minecraft/protocol/bedrock.rs b/crates/lib/src/games/minecraft/protocol/bedrock.rs index 119d8053..79ef42a1 100644 --- a/crates/lib/src/games/minecraft/protocol/bedrock.rs +++ b/crates/lib/src/games/minecraft/protocol/bedrock.rs @@ -3,11 +3,11 @@ use crate::{ buffer::{Buffer, Utf8Decoder}, games::minecraft::{BedrockResponse, GameMode, Server}, - protocols::types::TimeoutSettings, socket::{Socket, UdpSocket}, utils::{error_by_expected_size, retry_on_timeout}, GDErrorKind::{PacketBad, TypeParse}, GDResult, + TimeoutSettings, }; use std::net::SocketAddr; diff --git a/crates/lib/src/games/minecraft/protocol/java.rs b/crates/lib/src/games/minecraft/protocol/java.rs index 5edc8ca5..ca845665 100644 --- a/crates/lib/src/games/minecraft/protocol/java.rs +++ b/crates/lib/src/games/minecraft/protocol/java.rs @@ -1,11 +1,11 @@ use crate::{ buffer::Buffer, games::minecraft::{as_string, as_varint, get_string, get_varint, JavaResponse, Player, RequestSettings, Server}, - protocols::types::TimeoutSettings, socket::{Socket, TcpSocket}, utils::retry_on_timeout, GDErrorKind::{JsonParse, PacketBad}, GDResult, + TimeoutSettings, }; use byteorder::LittleEndian; diff --git a/crates/lib/src/games/minecraft/protocol/legacy_v1_4.rs b/crates/lib/src/games/minecraft/protocol/legacy_v1_4.rs index 0df738aa..f0d901f8 100644 --- a/crates/lib/src/games/minecraft/protocol/legacy_v1_4.rs +++ b/crates/lib/src/games/minecraft/protocol/legacy_v1_4.rs @@ -4,11 +4,11 @@ use crate::minecraft::protocol::legacy_v1_6::LegacyV1_6; use crate::{ buffer::{Buffer, Utf16Decoder}, games::minecraft::{JavaResponse, LegacyGroup, Server}, - protocols::types::TimeoutSettings, socket::{Socket, TcpSocket}, utils::{error_by_expected_size, retry_on_timeout}, GDErrorKind::{PacketBad, ProtocolFormat}, GDResult, + TimeoutSettings, }; use std::net::SocketAddr; diff --git a/crates/lib/src/games/minecraft/protocol/legacy_v1_6.rs b/crates/lib/src/games/minecraft/protocol/legacy_v1_6.rs index 3b53aac7..2a508c25 100644 --- a/crates/lib/src/games/minecraft/protocol/legacy_v1_6.rs +++ b/crates/lib/src/games/minecraft/protocol/legacy_v1_6.rs @@ -3,11 +3,11 @@ use byteorder::BigEndian; use crate::{ buffer::{Buffer, Utf16Decoder}, games::minecraft::{JavaResponse, LegacyGroup, Server}, - protocols::types::TimeoutSettings, socket::{Socket, TcpSocket}, utils::{error_by_expected_size, retry_on_timeout}, GDErrorKind::{PacketBad, ProtocolFormat}, GDResult, + TimeoutSettings, }; use std::net::SocketAddr; diff --git a/crates/lib/src/games/minecraft/protocol/legacy_vb1_8.rs b/crates/lib/src/games/minecraft/protocol/legacy_vb1_8.rs index 3ad21bae..f5f44b87 100644 --- a/crates/lib/src/games/minecraft/protocol/legacy_vb1_8.rs +++ b/crates/lib/src/games/minecraft/protocol/legacy_vb1_8.rs @@ -1,11 +1,11 @@ use crate::{ buffer::{Buffer, Utf16Decoder}, games::minecraft::{JavaResponse, LegacyGroup, Server}, - protocols::types::TimeoutSettings, socket::{Socket, TcpSocket}, utils::{error_by_expected_size, retry_on_timeout}, GDErrorKind::{PacketBad, ProtocolFormat}, GDResult, + TimeoutSettings, }; use std::net::SocketAddr; diff --git a/crates/lib/src/games/minecraft/protocol/mod.rs b/crates/lib/src/games/minecraft/protocol/mod.rs index 5beaeba9..c055cdf2 100644 --- a/crates/lib/src/games/minecraft/protocol/mod.rs +++ b/crates/lib/src/games/minecraft/protocol/mod.rs @@ -12,9 +12,9 @@ use crate::{ JavaResponse, LegacyGroup, }, - protocols::types::TimeoutSettings, GDErrorKind::AutoQuery, GDResult, + TimeoutSettings, }; use std::net::SocketAddr; diff --git a/crates/lib/src/games/mod.rs b/crates/lib/src/games/mod.rs index e8e56415..72bfeed0 100644 --- a/crates/lib/src/games/mod.rs +++ b/crates/lib/src/games/mod.rs @@ -26,9 +26,9 @@ pub mod theship; use crate::protocols::gamespy::GameSpyVersion; use crate::protocols::quake::QuakeVersion; -use crate::protocols::types::{CommonResponse, ExtraRequestSettings, ProprietaryProtocol, TimeoutSettings}; +use crate::protocols::types::{CommonResponse, ExtraRequestSettings, ProprietaryProtocol}; use crate::protocols::{self, Protocol}; -use crate::GDResult; +use crate::{GDResult, TimeoutSettings}; use std::net::{IpAddr, SocketAddr}; /// Definition of a game diff --git a/crates/lib/src/games/theship.rs b/crates/lib/src/games/theship.rs index 5084200f..7bb4967e 100644 --- a/crates/lib/src/games/theship.rs +++ b/crates/lib/src/games/theship.rs @@ -1,11 +1,12 @@ use crate::{ protocols::{ - types::{CommonPlayer, CommonResponse, GenericPlayer, TimeoutSettings}, + types::{CommonPlayer, CommonResponse, GenericPlayer}, valve::{self, get_optional_extracted_data, Server, ServerPlayer}, GenericResponse, }, GDErrorKind::PacketBad, GDResult, + TimeoutSettings, }; use std::net::{IpAddr, SocketAddr}; diff --git a/crates/lib/src/lib.rs b/crates/lib/src/lib.rs index 7cafba7c..48daa0c0 100644 --- a/crates/lib/src/lib.rs +++ b/crates/lib/src/lib.rs @@ -40,6 +40,7 @@ pub mod games; pub mod protocols; #[cfg(feature = "services")] pub mod services; +pub mod types; mod buffer; mod socket; @@ -50,3 +51,5 @@ pub use errors::*; pub use games::*; #[cfg(feature = "services")] pub use services::*; + +pub use types::*; diff --git a/crates/lib/src/protocols/gamespy/protocols/one/protocol.rs b/crates/lib/src/protocols/gamespy/protocols/one/protocol.rs index 1700b190..fe9d0979 100644 --- a/crates/lib/src/protocols/gamespy/protocols/one/protocol.rs +++ b/crates/lib/src/protocols/gamespy/protocols/one/protocol.rs @@ -7,13 +7,11 @@ use crate::GDErrorKind::TypeParse; use crate::utils::retry_on_timeout; use crate::{ buffer::Buffer, - protocols::{ - gamespy::one::{Player, Response}, - types::TimeoutSettings, - }, + protocols::gamespy::one::{Player, Response}, socket::{Socket, UdpSocket}, GDErrorKind, GDResult, + TimeoutSettings, }; use std::collections::HashMap; use std::net::SocketAddr; diff --git a/crates/lib/src/protocols/gamespy/protocols/three/protocol.rs b/crates/lib/src/protocols/gamespy/protocols/three/protocol.rs index d868d6bc..97d9bc6c 100644 --- a/crates/lib/src/protocols/gamespy/protocols/three/protocol.rs +++ b/crates/lib/src/protocols/gamespy/protocols/three/protocol.rs @@ -3,11 +3,11 @@ use byteorder::{BigEndian, LittleEndian}; use crate::buffer::{Buffer, Utf8Decoder}; use crate::protocols::gamespy::common::has_password; use crate::protocols::gamespy::three::{Player, Response, Team}; -use crate::protocols::types::TimeoutSettings; use crate::socket::{Socket, UdpSocket}; use crate::utils::retry_on_timeout; use crate::GDErrorKind::{PacketBad, TypeParse}; -use crate::{GDErrorKind, GDResult}; +use crate::{GDErrorKind, GDResult, TimeoutSettings}; + use std::collections::HashMap; use std::net::SocketAddr; diff --git a/crates/lib/src/protocols/gamespy/protocols/two/protocol.rs b/crates/lib/src/protocols/gamespy/protocols/two/protocol.rs index 81904db9..7c0577d7 100644 --- a/crates/lib/src/protocols/gamespy/protocols/two/protocol.rs +++ b/crates/lib/src/protocols/gamespy/protocols/two/protocol.rs @@ -1,10 +1,10 @@ use crate::buffer::{Buffer, Utf8Decoder}; use crate::protocols::gamespy::two::{Player, Response, Team}; -use crate::protocols::types::TimeoutSettings; use crate::socket::{Socket, UdpSocket}; use crate::utils::retry_on_timeout; use crate::GDErrorKind::{PacketBad, TypeParse}; -use crate::{GDErrorKind, GDResult}; +use crate::{GDErrorKind, GDResult, TimeoutSettings}; + use byteorder::BigEndian; use std::collections::HashMap; use std::net::SocketAddr; diff --git a/crates/lib/src/protocols/quake/client.rs b/crates/lib/src/protocols/quake/client.rs index 79c65f47..13390b37 100644 --- a/crates/lib/src/protocols/quake/client.rs +++ b/crates/lib/src/protocols/quake/client.rs @@ -2,11 +2,10 @@ use byteorder::LittleEndian; use crate::buffer::{Buffer, Utf8Decoder}; use crate::protocols::quake::types::Response; -use crate::protocols::types::TimeoutSettings; use crate::socket::{Socket, UdpSocket}; use crate::utils::retry_on_timeout; use crate::GDErrorKind::{PacketBad, TypeParse}; -use crate::{GDErrorKind, GDResult}; +use crate::{GDErrorKind, GDResult, TimeoutSettings}; use std::collections::HashMap; use std::net::SocketAddr; use std::slice::Iter; diff --git a/crates/lib/src/protocols/quake/one.rs b/crates/lib/src/protocols/quake/one.rs index 528b841b..78b04257 100644 --- a/crates/lib/src/protocols/quake/one.rs +++ b/crates/lib/src/protocols/quake/one.rs @@ -1,8 +1,9 @@ use crate::protocols::quake::client::{client_query, remove_wrapping_quotes, QuakeClient}; use crate::protocols::quake::Response; -use crate::protocols::types::{CommonPlayer, GenericPlayer, TimeoutSettings}; +use crate::protocols::types::{CommonPlayer, GenericPlayer}; use crate::GDErrorKind::TypeParse; -use crate::{GDErrorKind, GDResult}; +use crate::{GDErrorKind, GDResult, TimeoutSettings}; + #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::net::SocketAddr; diff --git a/crates/lib/src/protocols/quake/three.rs b/crates/lib/src/protocols/quake/three.rs index 68a25efe..9f056c20 100644 --- a/crates/lib/src/protocols/quake/three.rs +++ b/crates/lib/src/protocols/quake/three.rs @@ -1,8 +1,8 @@ use crate::protocols::quake::client::{client_query, QuakeClient}; use crate::protocols::quake::two::QuakeTwo; use crate::protocols::quake::Response; -use crate::protocols::types::TimeoutSettings; -use crate::GDResult; +use crate::{GDResult, TimeoutSettings}; + use std::net::SocketAddr; use std::slice::Iter; diff --git a/crates/lib/src/protocols/quake/two.rs b/crates/lib/src/protocols/quake/two.rs index d70daa48..268a4803 100644 --- a/crates/lib/src/protocols/quake/two.rs +++ b/crates/lib/src/protocols/quake/two.rs @@ -1,9 +1,9 @@ use crate::protocols::quake::client::{client_query, remove_wrapping_quotes, QuakeClient}; use crate::protocols::quake::one::QuakeOne; use crate::protocols::quake::Response; -use crate::protocols::types::{CommonPlayer, GenericPlayer, TimeoutSettings}; +use crate::protocols::types::{CommonPlayer, GenericPlayer}; use crate::GDErrorKind::TypeParse; -use crate::{GDErrorKind, GDResult}; +use crate::{GDErrorKind, GDResult, TimeoutSettings}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::net::SocketAddr; diff --git a/crates/lib/src/protocols/types.rs b/crates/lib/src/protocols/types.rs index 22ad776d..762070b8 100644 --- a/crates/lib/src/protocols/types.rs +++ b/crates/lib/src/protocols/types.rs @@ -1,8 +1,5 @@ +use crate::minecraft; use crate::protocols::{gamespy, quake, unreal2, valve}; -use crate::GDErrorKind::InvalidInput; -use crate::{minecraft, GDResult}; - -use std::time::Duration; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -146,91 +143,6 @@ pub struct CommonPlayerJson<'a> { pub score: Option, } -/// Timeout settings for socket operations -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct TimeoutSettings { - read: Option, - write: Option, - retries: usize, -} - -impl TimeoutSettings { - /// Construct new settings, passing None will block indefinitely. - /// Passing zero Duration throws GDErrorKind::[InvalidInput]. - /// - /// The retry count is the number of extra tries once the original request - /// fails, so a value of "0" will only make a single request, whereas - /// "1" will try the request again once if it fails. - /// The retry count is per-request so for multi-request queries (valve) if a - /// single part fails that part can be retried up to `retries` times. - pub fn new(read: Option, write: Option, retries: usize) -> GDResult { - if let Some(read_duration) = read { - if read_duration == Duration::new(0, 0) { - return Err(InvalidInput.context("Read duration must not be 0")); - } - } - - if let Some(write_duration) = write { - if write_duration == Duration::new(0, 0) { - return Err(InvalidInput.context("Write duration must not be 0")); - } - } - - Ok(Self { - read, - write, - retries, - }) - } - - /// Get the read timeout. - pub const fn get_read(&self) -> Option { self.read } - - /// Get the write timeout. - pub const fn get_write(&self) -> Option { self.write } - - /// Get number of retries - pub const fn get_retries(&self) -> usize { self.retries } - - /// Get the number of retries if there are timeout settings else fall back - /// to the default - pub const fn get_retries_or_default(timeout_settings: &Option) -> usize { - if let Some(timeout_settings) = timeout_settings { - timeout_settings.get_retries() - } else { - TimeoutSettings::const_default().get_retries() - } - } - - /// Get the read and write durations if there are timeout settings else fall - /// back to the defaults - pub const fn get_read_and_write_or_defaults( - timeout_settings: &Option, - ) -> (Option, Option) { - if let Some(timeout_settings) = timeout_settings { - (timeout_settings.get_read(), timeout_settings.get_write()) - } else { - let default = TimeoutSettings::const_default(); - (default.get_read(), default.get_write()) - } - } - - /// Default values are 4 seconds for both read and write, no retries. - pub const fn const_default() -> Self { - Self { - read: Some(Duration::from_secs(4)), - write: Some(Duration::from_secs(4)), - retries: 0, - } - } -} - -impl Default for TimeoutSettings { - /// Default values are 4 seconds for both read and write, no retries. - fn default() -> Self { Self::const_default() } -} - /// Generic extra request settings /// /// Fields of this struct may not be used depending on which protocol @@ -309,56 +221,16 @@ impl ExtraRequestSettings { } } +use crate::types::TimeoutSettings as NewTimeoutSettings; +#[deprecated( + since = "0.5.0", + note = "TimeoutSettings has been moved to gamedig::TimeoutSettings" +)] +pub type TimeoutSettings = NewTimeoutSettings; + #[cfg(test)] mod tests { use super::*; - use std::time::Duration; - - // Test creating new TimeoutSettings with valid durations - #[test] - fn test_new_with_valid_durations() -> GDResult<()> { - // Define valid read and write durations - let read_duration = Duration::from_secs(1); - let write_duration = Duration::from_secs(2); - - // Create new TimeoutSettings with the valid durations - let timeout_settings = TimeoutSettings::new(Some(read_duration), Some(write_duration), 0)?; - - // Verify that the get_read and get_write methods return the expected values - assert_eq!(timeout_settings.get_read(), Some(read_duration)); - assert_eq!(timeout_settings.get_write(), Some(write_duration)); - - Ok(()) - } - - // Test creating new TimeoutSettings with a zero duration - #[test] - fn test_new_with_zero_duration() { - // Define a zero read duration and a valid write duration - let read_duration = Duration::new(0, 0); - let write_duration = Duration::from_secs(2); - - // Try to create new TimeoutSettings with the zero read duration (this should - // fail) - let result = TimeoutSettings::new(Some(read_duration), Some(write_duration), 0); - - // Verify that the function returned an error and that the error type is - // InvalidInput - assert!(result.is_err()); - assert_eq!(result.unwrap_err(), crate::GDErrorKind::InvalidInput.into()); - } - - // Test that the default TimeoutSettings values are correct - #[test] - fn test_default_values() { - // Get the default TimeoutSettings values - let default_settings = TimeoutSettings::default(); - - // Verify that the get_read and get_write methods return the expected default - // values - assert_eq!(default_settings.get_read(), Some(Duration::from_secs(4))); - assert_eq!(default_settings.get_write(), Some(Duration::from_secs(4))); - } // Test that extra request settings can be converted #[test] diff --git a/crates/lib/src/protocols/unreal2/protocol.rs b/crates/lib/src/protocols/unreal2/protocol.rs index 46f16c81..3e1e8ef0 100644 --- a/crates/lib/src/protocols/unreal2/protocol.rs +++ b/crates/lib/src/protocols/unreal2/protocol.rs @@ -1,9 +1,8 @@ use crate::buffer::{Buffer, StringDecoder}; use crate::errors::GDErrorKind::PacketBad; -use crate::protocols::types::TimeoutSettings; use crate::socket::{Socket, UdpSocket}; use crate::utils::retry_on_timeout; -use crate::GDResult; +use crate::{GDResult, TimeoutSettings}; use super::{GatheringSettings, MutatorsAndRules, PacketKind, Players, Response, ServerInfo}; diff --git a/crates/lib/src/protocols/valve/protocol.rs b/crates/lib/src/protocols/valve/protocol.rs index 7fc44cb1..33bbd791 100644 --- a/crates/lib/src/protocols/valve/protocol.rs +++ b/crates/lib/src/protocols/valve/protocol.rs @@ -1,27 +1,25 @@ use crate::{ buffer::Buffer, - protocols::{ - types::TimeoutSettings, - valve::{ - types::{ - Environment, - ExtraData, - GatheringSettings, - Request, - Response, - Server, - ServerInfo, - ServerPlayer, - TheShip, - }, - Engine, - ModData, + protocols::valve::{ + types::{ + Environment, + ExtraData, + GatheringSettings, + Request, + Response, + Server, + ServerInfo, + ServerPlayer, + TheShip, }, + Engine, + ModData, }, socket::{Socket, UdpSocket}, utils::{retry_on_timeout, u8_lower_upper}, GDErrorKind::{BadGame, Decompress, UnknownEnumCast}, GDResult, + TimeoutSettings, }; use bzip2_rs::decoder::Decoder; diff --git a/crates/lib/src/socket.rs b/crates/lib/src/socket.rs index 36c4dca6..d7461cdf 100644 --- a/crates/lib/src/socket.rs +++ b/crates/lib/src/socket.rs @@ -1,7 +1,7 @@ use crate::{ - protocols::types::TimeoutSettings, GDErrorKind::{PacketReceive, PacketSend, SocketBind, SocketConnect}, GDResult, + TimeoutSettings, }; use std::net::SocketAddr; diff --git a/crates/lib/src/types.rs b/crates/lib/src/types.rs new file mode 100644 index 00000000..b7882a1d --- /dev/null +++ b/crates/lib/src/types.rs @@ -0,0 +1,146 @@ +//! Types useful for all parts of the library. + +use std::time::Duration; + +use crate::errors::GDErrorKind::InvalidInput; +use crate::GDResult; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Timeout settings for socket operations +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct TimeoutSettings { + read: Option, + write: Option, + retries: usize, +} + +impl TimeoutSettings { + /// Construct new settings, passing None will block indefinitely. + /// Passing zero Duration throws GDErrorKind::[InvalidInput]. + /// + /// The retry count is the number of extra tries once the original request + /// fails, so a value of "0" will only make a single request, whereas + /// "1" will try the request again once if it fails. + /// The retry count is per-request so for multi-request queries (valve) if a + /// single part fails that part can be retried up to `retries` times. + pub fn new(read: Option, write: Option, retries: usize) -> GDResult { + if let Some(read_duration) = read { + if read_duration == Duration::new(0, 0) { + return Err(InvalidInput.context("Read duration must not be 0")); + } + } + + if let Some(write_duration) = write { + if write_duration == Duration::new(0, 0) { + return Err(InvalidInput.context("Write duration must not be 0")); + } + } + + Ok(Self { + read, + write, + retries, + }) + } + + /// Get the read timeout. + pub const fn get_read(&self) -> Option { self.read } + + /// Get the write timeout. + pub const fn get_write(&self) -> Option { self.write } + + /// Get number of retries + pub const fn get_retries(&self) -> usize { self.retries } + + /// Get the number of retries if there are timeout settings else fall back + /// to the default + pub const fn get_retries_or_default(timeout_settings: &Option) -> usize { + if let Some(timeout_settings) = timeout_settings { + timeout_settings.get_retries() + } else { + TimeoutSettings::const_default().get_retries() + } + } + + /// Get the read and write durations if there are timeout settings else fall + /// back to the defaults + pub const fn get_read_and_write_or_defaults( + timeout_settings: &Option, + ) -> (Option, Option) { + if let Some(timeout_settings) = timeout_settings { + (timeout_settings.get_read(), timeout_settings.get_write()) + } else { + let default = TimeoutSettings::const_default(); + (default.get_read(), default.get_write()) + } + } + + /// Default values are 4 seconds for both read and write, no retries. + pub const fn const_default() -> Self { + Self { + read: Some(Duration::from_secs(4)), + write: Some(Duration::from_secs(4)), + retries: 0, + } + } +} + +impl Default for TimeoutSettings { + /// Default values are 4 seconds for both read and write, no retries. + fn default() -> Self { Self::const_default() } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::time::Duration; + + // Test creating new TimeoutSettings with valid durations + #[test] + fn test_new_with_valid_durations() -> GDResult<()> { + // Define valid read and write durations + let read_duration = Duration::from_secs(1); + let write_duration = Duration::from_secs(2); + + // Create new TimeoutSettings with the valid durations + let timeout_settings = TimeoutSettings::new(Some(read_duration), Some(write_duration), 0)?; + + // Verify that the get_read and get_write methods return the expected values + assert_eq!(timeout_settings.get_read(), Some(read_duration)); + assert_eq!(timeout_settings.get_write(), Some(write_duration)); + + Ok(()) + } + + // Test creating new TimeoutSettings with a zero duration + #[test] + fn test_new_with_zero_duration() { + // Define a zero read duration and a valid write duration + let read_duration = Duration::new(0, 0); + let write_duration = Duration::from_secs(2); + + // Try to create new TimeoutSettings with the zero read duration (this should + // fail) + let result = TimeoutSettings::new(Some(read_duration), Some(write_duration), 0); + + // Verify that the function returned an error and that the error type is + // InvalidInput + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), crate::GDErrorKind::InvalidInput.into()); + } + + // Test that the default TimeoutSettings values are correct + #[test] + fn test_default_values() { + // Get the default TimeoutSettings values + let default_settings = TimeoutSettings::default(); + + // Verify that the get_read and get_write methods return the expected default + // values + assert_eq!(default_settings.get_read(), Some(Duration::from_secs(4))); + assert_eq!(default_settings.get_write(), Some(Duration::from_secs(4))); + } +}