hypixel-sdk 0.2.0

Async client for the Hypixel Public API with typed models and SkyBlock helpers
Documentation
use std::io::Read;

use base64::Engine;
use flate2::read::GzDecoder;
use serde_json::Value;

/// Errors raised while decoding base64+gzip NBT blobs.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum NbtError {
    /// The input was not valid base64.
    #[error("invalid base64: {0}")]
    Base64(#[from] base64::DecodeError),
    /// The gzip stream could not be inflated.
    #[error("gzip decompression failed: {0}")]
    Gzip(#[source] std::io::Error),
    /// The decompressed bytes were not valid NBT.
    #[error("invalid NBT: {0}")]
    Nbt(#[from] fastnbt::error::Error),
    /// The expected `data` field was missing from an inventory blob.
    #[error("no NBT data field present")]
    MissingData,
}

/// Decode a base64-encoded, gzip-compressed NBT blob into an [`fastnbt::Value`].
///
/// SkyBlock serializes inventories and auction items this way (the `item_bytes`
/// field on auctions, and the `data` field within member inventory sections).
pub fn decode_nbt(encoded: &str) -> Result<fastnbt::Value, NbtError> {
    let compressed = base64::engine::general_purpose::STANDARD.decode(encoded.trim())?;
    let mut decoder = GzDecoder::new(&compressed[..]);
    let mut bytes = Vec::new();
    decoder.read_to_end(&mut bytes).map_err(NbtError::Gzip)?;
    Ok(fastnbt::from_bytes(&bytes)?)
}

/// Decode an inventory section such as `{ "type": 0, "data": "<base64>" }`.
///
/// Accepts either that object form or a bare base64 string.
pub fn decode_inventory(section: &Value) -> Result<fastnbt::Value, NbtError> {
    let encoded = match section {
        Value::String(s) => s.as_str(),
        Value::Object(map) => map
            .get("data")
            .and_then(Value::as_str)
            .ok_or(NbtError::MissingData)?,
        _ => return Err(NbtError::MissingData),
    };
    decode_nbt(encoded)
}