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}