lunar-lib 0.12.0

Common utilities for lunar applications
Documentation
// ID
/// The number of bytes in a given ID, to prevent use of 'magic numbers'
pub const ID_SIZE: usize = 32;

/// A 32 byte ID to represent a given type
#[derive(Debug)]
#[repr(transparent)]
#[cfg_attr(
    feature = "serde",
    derive(serde::Serialize, serde::Deserialize),
    serde(transparent)
)]
pub struct Id<T: ?Sized = ()>(
    #[cfg_attr(feature = "serde", serde(with = "hex::serde"))] [u8; ID_SIZE],
    std::marker::PhantomData<T>,
);

impl<T> Id<T> {
    #[must_use]
    /// Casts an [`Id<T>`] to an [`Id<U>`]
    pub fn cast<U>(self) -> Id<U> {
        Id::<U>(self.0, std::marker::PhantomData)
    }
}

#[cfg(feature = "rand")]
impl<T> rand::distr::Distribution<Id<T>> for rand::distr::StandardUniform {
    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Id<T> {
        use rand::RngExt;
        Id(rng.random(), std::marker::PhantomData)
    }
}

impl<T> Clone for Id<T> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<T> Copy for Id<T> {}

impl<T> PartialEq for Id<T> {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl<T> Eq for Id<T> {}

impl<T> PartialOrd for Id<T> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl<T> Ord for Id<T> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

impl<T> std::hash::Hash for Id<T> {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

impl<T> std::fmt::Display for Id<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        hex::encode(self.0).fmt(f)
    }
}

impl<T> TryFrom<&str> for Id<T> {
    type Error = hex::FromHexError;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        value.parse() // Defers to your FromStr implementation
    }
}

impl<T> TryFrom<String> for Id<T> {
    type Error = hex::FromHexError;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        value.parse()
    }
}

impl<T> std::str::FromStr for Id<T> {
    type Err = hex::FromHexError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        hex::FromHex::from_hex(s)
    }
}

impl<T> hex::FromHex for Id<T> {
    type Error = hex::FromHexError;

    fn from_hex<T1: AsRef<[u8]>>(hex: T1) -> Result<Self, Self::Error> {
        let id = <[u8; 32]>::from_hex(hex)?;
        Ok(Self(id, std::marker::PhantomData))
    }
}

impl<T> AsRef<[u8]> for Id<T> {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl<T> std::borrow::Borrow<[u8]> for Id<T> {
    fn borrow(&self) -> &[u8] {
        &self.0
    }
}

impl<T> std::borrow::Borrow<[u8; 32]> for Id<T> {
    fn borrow(&self) -> &[u8; 32] {
        &self.0
    }
}

impl<T> std::ops::Deref for Id<T> {
    type Target = [u8; 32];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T> From<[u8; 32]> for Id<T> {
    fn from(value: [u8; 32]) -> Self {
        Self(value, std::marker::PhantomData)
    }
}

impl<T> From<Id<T>> for [u8; 32] {
    fn from(value: Id<T>) -> Self {
        value.0
    }
}

impl<T> TryFrom<Vec<u8>> for Id<T> {
    type Error = std::array::TryFromSliceError;

    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
        Self::try_from(&*value)
    }
}

impl<T> TryFrom<&[u8]> for Id<T> {
    type Error = std::array::TryFromSliceError;

    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        let value: [u8; 32] = value.try_into()?;
        Ok(Self(value, std::marker::PhantomData))
    }
}

// ENTRY
/// An instance of [`T`] with a given [`Id<T>`]
#[derive(Debug, Clone, Copy)]
pub struct Entry<T> {
    entry: T,
    id: Id<T>,
}

impl<T> std::ops::Deref for Entry<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.entry
    }
}

impl<T> std::ops::DerefMut for Entry<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.entry
    }
}

impl<T> PartialEq for Entry<T> {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl<T> Eq for Entry<T> {}

impl<T> PartialOrd for Entry<T> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl<T> Ord for Entry<T> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.id.cmp(&other.id)
    }
}

impl<T> std::hash::Hash for Entry<T> {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.id.hash(state);
    }
}

impl<T> Entry<T> {
    /// Creates a new [`Entry<T>`] using the input [`T`] and [`Id<T>`]
    pub fn new(item: T, id: Id<T>) -> Self {
        Self { entry: item, id }
    }

    /// Returns the inner [`Id<T>`]
    pub fn id(&self) -> Id<T> {
        self.id
    }

    /// Consumes `self` to return the inner [`T`]
    pub fn into_inner(self) -> T {
        self.entry
    }
}