hypixel-sdk 0.2.0

Async client for the Hypixel Public API with typed models and SkyBlock helpers
Documentation
//! Username and UUID resolution against the Mojang API.
//!
//! Every keyed Hypixel endpoint takes a player UUID, but people know
//! usernames; these helpers bridge the two. They call Mojang, not Hypixel,
//! and need no API key.

use serde::Deserialize;

use crate::error::{Error, Result};

const PROFILE_URL: &str = "https://api.mojang.com/users/profiles/minecraft";
const SESSION_URL: &str = "https://sessionserver.mojang.com/session/minecraft/profile";

#[derive(Deserialize)]
struct MojangProfile {
    id: String,
    name: String,
}

/// Resolve a Minecraft username to its undashed UUID.
///
/// Returns `Ok(None)` when no account has that name.
pub async fn uuid_for_username(username: &str) -> Result<Option<String>> {
    Ok(fetch_profile(&format!("{PROFILE_URL}/{username}"))
        .await?
        .map(|p| p.id))
}

/// Resolve a player UUID (dashed or undashed) to its current username.
///
/// Returns `Ok(None)` when the UUID matches no account.
pub async fn username_for_uuid(uuid: &str) -> Result<Option<String>> {
    let uuid = uuid.replace('-', "");
    Ok(fetch_profile(&format!("{SESSION_URL}/{uuid}"))
        .await?
        .map(|p| p.name))
}

async fn fetch_profile(url: &str) -> Result<Option<MojangProfile>> {
    let response = reqwest::get(url).await?;
    let status = response.status();
    if status.as_u16() == 404 || status.as_u16() == 204 {
        return Ok(None);
    }
    if !status.is_success() {
        return Err(Error::Api {
            status: status.as_u16(),
            cause: "Mojang API error".to_string(),
        });
    }
    let bytes = response.bytes().await?;
    Ok(Some(serde_json::from_slice(&bytes)?))
}