rust-mc-status 3.0.0

High-performance asynchronous Rust library for querying Minecraft server status (Java & Bedrock)
Documentation
//! Address string parsing for Minecraft server addresses.
//!
//! Handles all common address formats:
//!
//! | Input | Host | Port | Explicit port |
//! |-------|------|------|---------------|
//! | `"mc.hypixel.net"` | `"mc.hypixel.net"` | default | `false` |
//! | `"mc.hypixel.net:19132"` | `"mc.hypixel.net"` | `19132` | `true` |
//! | `"192.168.1.1"` | `"192.168.1.1"` | default | `false` |
//! | `"192.168.1.1:25566"` | `"192.168.1.1"` | `25566` | `true` |
//! | `"[::1]"` | `"::1"` | default | `false` |
//! | `"[::1]:25565"` | `"::1"` | `25565` | `true` |
//! | `"::1"` (bare) | `"::1"` | default | `false` |

use crate::error::McError;

/// Parse an address string into `(host, port, explicit_port)`.
///
/// `explicit_port` is `true` when a port was present in the string, `false`
/// when `default_port` was used as a fallback.  This flag is used by
/// [`McClient`](crate::McClient) to decide whether to attempt an SRV lookup —
/// SRV is skipped when the caller already specified an explicit port.
///
/// # Supported formats
///
/// - `"hostname"` — uses `default_port`
/// - `"hostname:port"` — uses the given port
/// - `"[::1]"` — IPv6 without port, uses `default_port`
/// - `"[::1]:port"` — IPv6 with port
/// - `"::1"` — bare IPv6 (detected by multiple colons), uses `default_port`
///
/// # Errors
///
/// - [`McError::Config(InvalidAddress)`](crate::error::ConfigError::InvalidAddress)
///   — IPv6 address is missing the closing `]`.
/// - [`McError::Config(InvalidPort)`](crate::error::ConfigError::InvalidPort)
///   — port string cannot be parsed as `u16`.
pub fn parse(addr: &str, default_port: u16) -> Result<(&str, u16, bool), McError> {
    // IPv6 literal: [::1] or [::1]:port
    if let Some(rest) = addr.strip_prefix('[') {
        let end = rest
            .find(']')
            .ok_or_else(|| McError::invalid_address("missing ']' in IPv6 address"))?;
        let host  = &rest[..end];
        let after = &rest[end + 1..];
        if let Some(p) = after.strip_prefix(':') {
            let port = p
                .parse::<u16>()
                .map_err(|e| McError::invalid_port(e.to_string()))?;
            return Ok((host, port, true));
        }
        return Ok((host, default_port, false));
    }
    // Bare IPv6: more than one colon and no brackets — treat as host only
    if addr.chars().filter(|&c| c == ':').count() > 1 {
        return Ok((addr, default_port, false));
    }
    // hostname:port or plain hostname
    match addr.split_once(':') {
        Some((h, p)) => {
            let port = p
                .parse::<u16>()
                .map_err(|e| McError::invalid_port(e.to_string()))?;
            Ok((h, port, true))
        }
        None => Ok((addr, default_port, false)),
    }
}