rust-mc-status 3.0.0

High-performance asynchronous Rust library for querying Minecraft server status (Java & Bedrock)
Documentation
//! Basic usage example for the rust-mc-status library.
//!
//! Run with:
//!   cargo run --example basic_usage

use rust_mc_status::{ping_java, ping_bedrock, McClient, ServerEdition, StatusExt};
use std::time::Duration;

fn sep() {
    println!("\n{}\n", "".repeat(50));
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("=== rust-mc-status ===\n");

    // ─── 1. Facade — no client needed ────────────────────────────────────────
    //
    // ping_java / ping_bedrock use a shared global client under the hood.
    // .motd_clean() strips Minecraft §-color codes automatically.

    println!("1. Facade — no client needed");
    match ping_java("mc.hypixel.net").await {
        Ok(s)  => println!("{} | {} ms | {}", s.display_players(), s.latency_ms() as u32, s.motd_clean()),
        Err(e) => println!("   ❌ Java: {e}"),
    }
    match ping_bedrock("geo.hivebedrock.network").await {
        Ok(s)  => println!("{} | {} ms | {}", s.display_players(), s.latency_ms() as u32, s.motd_clean()),
        Err(e) => println!("   ❌ Bedrock: {e}"),
    }

    sep();

    // ─── 2. Client with custom settings ──────────────────────────────────────

    let client = McClient::builder()
        .timeout(Duration::from_secs(15))
        .max_parallel(10)
        .build();

    // ─── 3. Java — full typed status ─────────────────────────────────────────

    println!("2. Java — typed status");
    match client.java("mc.hypixel.net").await {
        Ok(s) => {
            println!("   IP       : {}", s.ip());
            println!("   Port     : {}", s.port());
            println!("   Version  : {}", s.version());
            println!("   Players  : {}", s.display_players());
            println!("   Latency  : {:.0} ms", s.latency_ms());
            println!("   MOTD     : {}", s.motd_clean());
            if let Some(fav) = s.favicon() {
                println!("   Favicon  : {} bytes (base64 PNG)", fav.len());
            }
        }
        Err(e) => println!("{e}"),
    }

    sep();

    // ─── 4. Per-request timeout override ─────────────────────────────────────

    println!("3. Java — per-request timeout (10 s)");
    match client.java("mc.hypixel.net").timeout(Duration::from_secs(10)).await {
        Ok(s)  => println!("{} ms — {}", s.latency_ms() as u32, s.display_players()),
        Err(e) => println!("{e}"),
    }

    sep();

    // ─── 5. Bedrock — full typed status ──────────────────────────────────────

    println!("4. Bedrock — typed status");
    match client.bedrock("geo.hivebedrock.network:19132").await {
        Ok(s) => {
            println!("   Edition  : {}", s.edition());
            println!("   Version  : {}", s.version());
            println!("   Players  : {}", s.display_players());
            println!("   Latency  : {:.0} ms", s.latency_ms());
            println!("   MOTD     : {}", s.motd_clean());
            println!("   MOTD2    : {}", s.motd2_clean());
            println!("   Game mode: {}", s.game_mode());
        }
        Err(e) => println!("{e}"),
    }

    sep();

    // ─── 6. Serialize to JSON ─────────────────────────────────────────────────
    //
    // Both JavaServerStatus and BedrockServerStatus implement serde::Serialize
    // via #[serde(transparent)] — the wrapper is invisible in JSON output.
    //
    // favicon and raw_data are excluded automatically (#[serde(skip)]) to keep
    // the output clean. Use .favicon() or .raw() if you need them separately.

    println!("5. Serialize to JSON");
    match client.java("mc.hypixel.net").await {
        Ok(s) => {
            // Compact — ready for HTTP APIs, message queues, etc.
            let compact = serde_json::to_string(&s)?;
            println!("   compact  : {}", &compact[..120.min(compact.len())]);

            // Pretty — human-readable
            let pretty = serde_json::to_string_pretty(&s)?;
            println!("   pretty   :\n{}", pretty
                .lines()
                .take(12)
                .map(|l| format!("     {l}"))
                .collect::<Vec<_>>()
                .join("\n"));
            println!("     … ({} bytes total)", compact.len());
        }
        Err(e) => println!("{e}"),
    }

    // Bedrock serializes the same way
    match client.bedrock("geo.hivebedrock.network:19132").await {
        Ok(s) => {
            let json = serde_json::to_string(&s)?;
            println!("\n   bedrock  : {}", &json[..120.min(json.len())]);
        }
        Err(e) => println!("\n   bedrock  : ❌ {e}"),
    }

    sep();

    // ─── 7. Reachability check — is_online ───────────────────────────────────

    println!("6. Reachability check");
    let edition: ServerEdition = "java".parse()?;
    let online = client
        .server("mc.hypixel.net", edition)
        .timeout(Duration::from_secs(10))
        .is_online()
        .await;
    println!("   mc.hypixel.net online: {online}");

    sep();

    // ─── 8. Custom address type — Into<String> ───────────────────────────────

    println!("7. Custom address type (Into<String>)");
    struct Host { addr: &'static str }
    impl From<Host> for String {
        fn from(h: Host) -> Self { h.addr.to_string() }
    }
    match client.java(Host { addr: "mc.hypixel.net" }).await {
        Ok(s)  => println!("{} ms — {}", s.latency_ms() as u32, s.display_players()),
        Err(e) => println!("{e}"),
    }

    sep();

    // ─── 9. Raw ServerStatus ─────────────────────────────────────────────────

    println!("8. Raw ServerStatus");
    if let Ok(s) = client.java("mc.hypixel.net").await {
        let raw = s.raw();
        println!("   hostname : {}", raw.hostname);
        println!("   ip       : {}", raw.ip);
        println!("   port     : {}", raw.port);
        println!("   dns      : {:?}", raw.dns.as_ref().map(|d| &d.a_records));
    }

    sep();

    // ─── 10. Error handling ───────────────────────────────────────────────────

    println!("9. Error handling — unreachable server");
    match client.java("does-not-exist.invalid").timeout(Duration::from_secs(3)).await {
        Ok(s)  => println!("   ✅ Unexpected: {}", s.display_players()),
        Err(e) => println!("{} — this is expected", e),
    }

    Ok(())
}