Skip to main content

rust_mc_status/client/
builder.rs

1//! Per-request ping builders.
2//!
3//! Each builder is returned by the corresponding [`McClient`](crate::McClient)
4//! method and implements [`IntoFuture`](std::future::IntoFuture) so it can be
5//! `.await`-ed directly.
6//!
7//! | Builder | Created by | Returns |
8//! |---------|-----------|---------|
9//! | [`JavaPingBuilder`] | [`McClient::java`](crate::McClient::java) | [`JavaServerStatus`](crate::JavaServerStatus) |
10//! | [`BedrockPingBuilder`] | [`McClient::bedrock`](crate::McClient::bedrock) | [`BedrockServerStatus`](crate::BedrockServerStatus) |
11//! | [`ServerPingBuilder`] | [`McClient::server`](crate::McClient::server) | [`ServerStatus`](crate::ServerStatus) |
12//!
13//! All builders support a per-request `.timeout()` override that applies only
14//! to that single ping without affecting the client's global timeout.
15
16use std::future::{Future, IntoFuture};
17use std::pin::Pin;
18use std::time::Duration;
19
20use super::McClient;
21use crate::error::McError;
22use crate::models::ServerEdition;
23use crate::status::{BedrockServerStatus, JavaServerStatus};
24
25// ─── Java builder ─────────────────────────────────────────────────────────────
26
27/// Builder for a Java Edition ping request.
28///
29/// Constructed by [`McClient::java`]. Allows per-request overrides
30/// (e.g. timeout) without modifying the client's global config.
31///
32/// Call `.await` to execute.
33///
34/// # Example
35/// ```rust,no_run
36/// # #[tokio::main] async fn main() -> Result<(), rust_mc_status::McError> {
37/// use rust_mc_status::{McClient, StatusExt};
38/// use std::time::Duration;
39///
40/// let client = McClient::builder().build();
41///
42/// // Default timeout from client config
43/// let status = client.java("mc.hypixel.net").await?;
44///
45/// // Per-request timeout override
46/// let status = client.java("mc.hypixel.net")
47///     .timeout(Duration::from_secs(3))
48///     .await?;
49/// # Ok(()) }
50/// ```
51pub struct JavaPingBuilder {
52    client: McClient,
53    address: String,
54    timeout: Option<Duration>,
55}
56
57impl JavaPingBuilder {
58    pub(crate) fn new(client: McClient, address: impl Into<String>) -> Self {
59        Self {
60            client,
61            address: address.into(),
62            timeout: None,
63        }
64    }
65
66    /// Override the timeout for this request only.
67    ///
68    /// If not set, uses the client's configured timeout.
69    pub fn timeout(mut self, duration: Duration) -> Self {
70        self.timeout = Some(duration);
71        self
72    }
73}
74
75impl IntoFuture for JavaPingBuilder {
76    type Output = Result<JavaServerStatus, McError>;
77    type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
78
79    fn into_future(self) -> Self::IntoFuture {
80        Box::pin(async move {
81            // Apply per-request timeout override if present
82            let client = match self.timeout {
83                Some(t) => { let mut c = self.client.clone(); c.timeout = t; c },
84                None => self.client.clone(),
85            };
86            client.ping_java_inner(&self.address).await
87        })
88    }
89}
90
91// ─── Bedrock builder ──────────────────────────────────────────────────────────
92
93/// Builder for a Bedrock Edition ping request.
94///
95/// Constructed by [`McClient::bedrock`]. Allows per-request overrides
96/// (e.g. timeout) without modifying the client's global config.
97///
98/// Call `.await` to execute.
99///
100/// # Example
101/// ```rust,no_run
102/// # #[tokio::main] async fn main() -> Result<(), rust_mc_status::McError> {
103/// use rust_mc_status::{McClient, StatusExt};
104/// use std::time::Duration;
105///
106/// let client = McClient::builder().build();
107///
108/// let status = client.bedrock("play.cubecraft.net")
109///     .timeout(Duration::from_secs(5))
110///     .await?;
111///
112/// println!("{}", status.display_players());
113/// # Ok(()) }
114/// ```
115pub struct BedrockPingBuilder {
116    client: McClient,
117    address: String,
118    timeout: Option<Duration>,
119}
120
121impl BedrockPingBuilder {
122    pub(crate) fn new(client: McClient, address: impl Into<String>) -> Self {
123        Self {
124            client,
125            address: address.into(),
126            timeout: None,
127        }
128    }
129
130    /// Override the timeout for this request only.
131    pub fn timeout(mut self, duration: Duration) -> Self {
132        self.timeout = Some(duration);
133        self
134    }
135}
136
137impl IntoFuture for BedrockPingBuilder {
138    type Output = Result<BedrockServerStatus, McError>;
139    type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
140
141    fn into_future(self) -> Self::IntoFuture {
142        Box::pin(async move {
143            let client = match self.timeout {
144                Some(t) => { let mut c = self.client.clone(); c.timeout = t; c },
145                None => self.client.clone(),
146            };
147            client.ping_bedrock_inner(&self.address).await
148        })
149    }
150}
151
152// ─── Generic builder (edition chosen at runtime) ──────────────────────────────
153
154/// Builder for a ping request where the edition is chosen at runtime.
155///
156/// Constructed by [`McClient::server`]. Useful when edition comes
157/// from user input or config files.
158///
159/// # Example
160/// ```rust,no_run
161/// # #[tokio::main] async fn main() -> Result<(), rust_mc_status::McError> {
162/// use rust_mc_status::{McClient, ServerEdition};
163/// use std::time::Duration;
164///
165/// let client = McClient::builder().build();
166/// let edition: ServerEdition = "java".parse()?;
167///
168/// let is_online = client
169///     .server("mc.hypixel.net", edition)
170///     .timeout(Duration::from_secs(3))
171///     .is_online()
172///     .await;
173///
174/// println!("Online: {}", is_online);
175/// # Ok(()) }
176/// ```
177pub struct ServerPingBuilder {
178    client: McClient,
179    address: String,
180    edition: ServerEdition,
181    timeout: Option<Duration>,
182}
183
184impl ServerPingBuilder {
185    pub(crate) fn new(
186        client: McClient,
187        address: impl Into<String>,
188        edition: ServerEdition,
189    ) -> Self {
190        Self {
191            client,
192            address: address.into(),
193            edition,
194            timeout: None,
195        }
196    }
197
198    /// Override the timeout for this request only.
199    pub fn timeout(mut self, duration: Duration) -> Self {
200        self.timeout = Some(duration);
201        self
202    }
203
204    /// Returns `true` if the server responds within the timeout,
205    /// without allocating a full status object.
206    pub async fn is_online(self) -> bool {
207        self.await.is_ok()
208    }
209}
210
211impl IntoFuture for ServerPingBuilder {
212    type Output = Result<crate::models::ServerStatus, McError>;
213    type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
214
215    fn into_future(self) -> Self::IntoFuture {
216        Box::pin(async move {
217            let client = match self.timeout {
218                Some(t) => { let mut c = self.client.clone(); c.timeout = t; c },
219                None => self.client.clone(),
220            };
221            match self.edition {
222                ServerEdition::Java => {
223                    client.ping_java_inner(&self.address).await.map(|s| s.0)
224                }
225                ServerEdition::Bedrock => {
226                    client.ping_bedrock_inner(&self.address).await.map(|s| s.0)
227                }
228            }
229        })
230    }
231}