rust-mc-status 3.0.0

High-performance asynchronous Rust library for querying Minecraft server status (Java & Bedrock)
Documentation
//! Per-request ping builders.
//!
//! Each builder is returned by the corresponding [`McClient`](crate::McClient)
//! method and implements [`IntoFuture`](std::future::IntoFuture) so it can be
//! `.await`-ed directly.
//!
//! | Builder | Created by | Returns |
//! |---------|-----------|---------|
//! | [`JavaPingBuilder`] | [`McClient::java`](crate::McClient::java) | [`JavaServerStatus`](crate::JavaServerStatus) |
//! | [`BedrockPingBuilder`] | [`McClient::bedrock`](crate::McClient::bedrock) | [`BedrockServerStatus`](crate::BedrockServerStatus) |
//! | [`ServerPingBuilder`] | [`McClient::server`](crate::McClient::server) | [`ServerStatus`](crate::ServerStatus) |
//!
//! All builders support a per-request `.timeout()` override that applies only
//! to that single ping without affecting the client's global timeout.

use std::future::{Future, IntoFuture};
use std::pin::Pin;
use std::time::Duration;

use super::McClient;
use crate::error::McError;
use crate::models::ServerEdition;
use crate::status::{BedrockServerStatus, JavaServerStatus};

// ─── Java builder ─────────────────────────────────────────────────────────────

/// Builder for a Java Edition ping request.
///
/// Constructed by [`McClient::java`]. Allows per-request overrides
/// (e.g. timeout) without modifying the client's global config.
///
/// Call `.await` to execute.
///
/// # Example
/// ```rust,no_run
/// # #[tokio::main] async fn main() -> Result<(), rust_mc_status::McError> {
/// use rust_mc_status::{McClient, StatusExt};
/// use std::time::Duration;
///
/// let client = McClient::builder().build();
///
/// // Default timeout from client config
/// let status = client.java("mc.hypixel.net").await?;
///
/// // Per-request timeout override
/// let status = client.java("mc.hypixel.net")
///     .timeout(Duration::from_secs(3))
///     .await?;
/// # Ok(()) }
/// ```
pub struct JavaPingBuilder {
    client: McClient,
    address: String,
    timeout: Option<Duration>,
}

impl JavaPingBuilder {
    pub(crate) fn new(client: McClient, address: impl Into<String>) -> Self {
        Self {
            client,
            address: address.into(),
            timeout: None,
        }
    }

    /// Override the timeout for this request only.
    ///
    /// If not set, uses the client's configured timeout.
    pub fn timeout(mut self, duration: Duration) -> Self {
        self.timeout = Some(duration);
        self
    }
}

impl IntoFuture for JavaPingBuilder {
    type Output = Result<JavaServerStatus, McError>;
    type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;

    fn into_future(self) -> Self::IntoFuture {
        Box::pin(async move {
            // Apply per-request timeout override if present
            let client = match self.timeout {
                Some(t) => { let mut c = self.client.clone(); c.timeout = t; c },
                None => self.client.clone(),
            };
            client.ping_java_inner(&self.address).await
        })
    }
}

// ─── Bedrock builder ──────────────────────────────────────────────────────────

/// Builder for a Bedrock Edition ping request.
///
/// Constructed by [`McClient::bedrock`]. Allows per-request overrides
/// (e.g. timeout) without modifying the client's global config.
///
/// Call `.await` to execute.
///
/// # Example
/// ```rust,no_run
/// # #[tokio::main] async fn main() -> Result<(), rust_mc_status::McError> {
/// use rust_mc_status::{McClient, StatusExt};
/// use std::time::Duration;
///
/// let client = McClient::builder().build();
///
/// let status = client.bedrock("play.cubecraft.net")
///     .timeout(Duration::from_secs(5))
///     .await?;
///
/// println!("{}", status.display_players());
/// # Ok(()) }
/// ```
pub struct BedrockPingBuilder {
    client: McClient,
    address: String,
    timeout: Option<Duration>,
}

impl BedrockPingBuilder {
    pub(crate) fn new(client: McClient, address: impl Into<String>) -> Self {
        Self {
            client,
            address: address.into(),
            timeout: None,
        }
    }

    /// Override the timeout for this request only.
    pub fn timeout(mut self, duration: Duration) -> Self {
        self.timeout = Some(duration);
        self
    }
}

impl IntoFuture for BedrockPingBuilder {
    type Output = Result<BedrockServerStatus, McError>;
    type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;

    fn into_future(self) -> Self::IntoFuture {
        Box::pin(async move {
            let client = match self.timeout {
                Some(t) => { let mut c = self.client.clone(); c.timeout = t; c },
                None => self.client.clone(),
            };
            client.ping_bedrock_inner(&self.address).await
        })
    }
}

// ─── Generic builder (edition chosen at runtime) ──────────────────────────────

/// Builder for a ping request where the edition is chosen at runtime.
///
/// Constructed by [`McClient::server`]. Useful when edition comes
/// from user input or config files.
///
/// # Example
/// ```rust,no_run
/// # #[tokio::main] async fn main() -> Result<(), rust_mc_status::McError> {
/// use rust_mc_status::{McClient, ServerEdition};
/// use std::time::Duration;
///
/// let client = McClient::builder().build();
/// let edition: ServerEdition = "java".parse()?;
///
/// let is_online = client
///     .server("mc.hypixel.net", edition)
///     .timeout(Duration::from_secs(3))
///     .is_online()
///     .await;
///
/// println!("Online: {}", is_online);
/// # Ok(()) }
/// ```
pub struct ServerPingBuilder {
    client: McClient,
    address: String,
    edition: ServerEdition,
    timeout: Option<Duration>,
}

impl ServerPingBuilder {
    pub(crate) fn new(
        client: McClient,
        address: impl Into<String>,
        edition: ServerEdition,
    ) -> Self {
        Self {
            client,
            address: address.into(),
            edition,
            timeout: None,
        }
    }

    /// Override the timeout for this request only.
    pub fn timeout(mut self, duration: Duration) -> Self {
        self.timeout = Some(duration);
        self
    }

    /// Returns `true` if the server responds within the timeout,
    /// without allocating a full status object.
    pub async fn is_online(self) -> bool {
        self.await.is_ok()
    }
}

impl IntoFuture for ServerPingBuilder {
    type Output = Result<crate::models::ServerStatus, McError>;
    type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;

    fn into_future(self) -> Self::IntoFuture {
        Box::pin(async move {
            let client = match self.timeout {
                Some(t) => { let mut c = self.client.clone(); c.timeout = t; c },
                None => self.client.clone(),
            };
            match self.edition {
                ServerEdition::Java => {
                    client.ping_java_inner(&self.address).await.map(|s| s.0)
                }
                ServerEdition::Bedrock => {
                    client.ping_bedrock_inner(&self.address).await.map(|s| s.0)
                }
            }
        })
    }
}