gwid 0.0.4

RFC 9562-compliant UUIDs
Documentation
pub mod v4;
pub mod v7;

pub(crate) mod nums;

/// Encodes a `u128` as UUID.
/// Formats as hexadecimal and inserts hyphens at proper indices.
pub fn encode(n: u128) -> String {
    let uuid = format!("{n:032x}");
    format!(
        "{}-{}-{}-{}-{}",
        // 8 is index of first hyphen
        &uuid[..8],
        // 12 is index of second hyphen
        &uuid[8..12],
        // 16 is index of third hyphen
        &uuid[12..16],
        // 20 is index of fourth hyphen
        &uuid[16..20],
        &uuid[20..]
    )
}

/// Unchecked method! Panics if something goes wrong.
/// Converts a `uuid` string into a u128.
pub fn uuid_to_raw_unchecked(uuid: &str) -> u128 {
    u128::from_str_radix(&uuid.replace("-", ""), 16).unwrap()
}

/// Converts a `uuid` string into a u128.
pub fn uuid_to_raw(uuid: &str) -> Result<u128, GWIDError> {
    Ok(u128::from_str_radix(&uuid.replace("-", ""), 16)?)
}

// Clears UUID Version bits (bits 48, 49, 50, and 51 when counting left->right and starting at 0)
// 11111111111111111111111111111111111111111111111100001111111111111111111111111111111111111111111111111111111111111111111111111111
pub(crate) const CLEAR_VERSION_MASK: u128 = 0xFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFF;
// Clears UUID Variant bits (bits 64 and 65 when counting left->right and starting at 0)
// 11111111111111111111111111111111111111111111111111111111111111110011111111111111111111111111111111111111111111111111111111111111
pub(crate) const CLEAR_VARIANT_MASK: u128 = 0xFFFFFFFFFFFFFFFF3FFFFFFFFFFFFFFF;
pub(crate) const VERSION_SHIFT: u128 = 76;
pub(crate) const VARIANT_SHIFT: u128 = 62;

#[derive(Debug)]
pub enum GWIDError {
    ParseUUIDError(std::num::ParseIntError),
}

impl std::fmt::Display for GWIDError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use GWIDError::*;
        match self {
            ParseUUIDError(parse_int_error) => write!(f, "Error parsing UUID {parse_int_error}"),
        }
    }
}

impl From<std::num::ParseIntError> for GWIDError {
    fn from(error: std::num::ParseIntError) -> Self {
        Self::ParseUUIDError(error)
    }
}

impl std::error::Error for GWIDError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Self::ParseUUIDError(parse_int_err) => Some(parse_int_err),
            // _ => None,
        }
    }
}

// Helper function to print bits
#[allow(dead_code)]
pub(crate) fn print_bits<T: std::fmt::Binary>(value: T) {
    let width = std::mem::size_of::<T>() * 8;
    let bits = format!("{value:0width$b}");
    for (i, byte) in bits.as_bytes().chunks(8).enumerate() {
        if i > 0 {
            print!(" ");
        }
        print!("{}", std::str::from_utf8(byte).unwrap());
    }
    println!();
}

#[cfg(test)]
mod lib_test {
    use super::*;

    #[test]
    fn encode_raw_uuid() {
        let raw: u128 = 213409605541016776915642275756511741574;
        let expect: &str = "a08d31e9-c256-443e-8425-73c382264686";
        let got = encode(raw);
        assert_eq!(
            expect, &got,
            "add_hyphens_to_raw_uuid | expected= {expect} got= {got}"
        );
    }

    // Tests both unchecked and "regular" variants.
    #[test]
    fn convert_uuid_to_u128() {
        let uuid = "a08d31e9-c256-443e-8425-73c382264686";
        let expect: u128 = 213409605541016776915642275756511741574;
        let got = uuid_to_raw_unchecked(uuid);
        assert_eq!(
            expect, got,
            "convert_uuid_to_u128_unchecked | expected= {expect} got= {got}"
        );
        let got = uuid_to_raw(uuid).unwrap();
        assert_eq!(
            expect, got,
            "convert_uuid_to_u128 | expected= {expect} got= {got}"
        );
    }
}