orthodoxy 0.2.4

A collection of my utility libraries
Documentation
use base32::Alphabet;
use facet::{Facet, Shape};
use rand::{Rng, RngExt, rng};
use serde::{Deserialize, Serialize};
use std::{fmt::Display, marker::PhantomData};
use tracing::{debug, error, info, warn};

#[derive(thiserror::Error, Debug)]
pub enum IdErrors {
    #[error(transparent)]
    StdIo(#[from] std::io::Error),

    #[error("{0}")]
    Custom(String),
}

impl From<String> for IdErrors {
    fn from(value: String) -> Self {
        Self::Custom(value)
    }
}

impl From<&str> for IdErrors {
    fn from(value: &str) -> Self {
        Self::from(value.to_string())
    }
}

pub type Res<T> = Result<T, IdErrors>;

#[derive(
    facet::Facet, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug, Serialize, Deserialize,
)]
pub struct Id<T, const N: usize = 12> {
    _type: PhantomData<T>,
    #[serde(with = "serde_arrays")]
    inner: [u8; N],
}

unsafe impl<T> Send for Id<T> {}
unsafe impl<T> Sync for Id<T> {}

// impl<'facet, T: Facet<'facet>, const N: usize> Display for Id<T, N> {
//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
//         write!(f, "{}", self.to_hex_strng())
//     }
// }

impl<'facet, T: Facet<'facet>, const N: usize> TryFrom<String> for Id<T, N> {
    type Error = IdErrors;

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

impl<'facet, T: Facet<'facet>, const N: usize> TryFrom<&str> for Id<T, N> {
    type Error = IdErrors;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        //let typ = format!("{}/", T::SHAPE.type_identifier);
        //let prefix_p = value.starts_with(&typ);
        let splt: Vec<&str> = value.split("/").collect();
        if splt.len() == 2 {
            let prefix = splt[0];
            let inner_str = splt[1];

            if prefix == T::SHAPE.type_identifier {
                let a = Alphabet::Rfc4648HexLower { padding: false };
                if let Some(o) = base32::decode(a, inner_str) {
                    let mut temp = [0u8; N];
                    temp.clone_from_slice(&o[0..N]);
                    Ok(Self::new(temp))
                } else {
                    Err(format!("Failed to parse '{}' as b32hex without padding", inner_str).into())
                }
            } else {
                let m = format!(
                    "Expected '{}' type prefix, but got '{prefix}' prefix",
                    T::SHAPE.type_identifier
                );
                warn!(m);
                Err(m.into())
            }
        } else {
            return Err(format!(
                "Value contains <{}>, but it should be exactly 2",
                splt.len()
            )
            .into());
        }
    }
}

impl<'facet, const N: usize, T: Facet<'facet>> Id<T, N> {
    pub fn prefix_str() -> &'static str {
        T::SHAPE.type_identifier
    }

    pub fn to_hex_strng(&self) -> String {
        let mut o = format!("{}/", T::SHAPE.type_identifier);
        for i in self.inner {
            o = format!("{o}{i:0>2X}");
        }
        o
    }
    pub fn into_hex_strng(self) -> String {
        let mut o = format!("{}/", T::SHAPE.type_identifier);
        for i in self.inner {
            o = format!("{o}{i:0>2X}");
        }
        o
    }

    pub fn from_hex_strng(s: &str) -> Res<Self> {
        todo!()
    }

    pub fn prefix_match(s: &str) -> bool {
        s.starts_with(Self::prefix_str())
    }

    fn strip_prefix(s: &str) -> Option<&str> {
        if let Some(p) = s.strip_prefix(Self::prefix_str())
            && let Some(pp) = p.strip_prefix("/")
        {
            // assert!(
            //     pp.len() == (s.len() - (Self::prefix_str().len() - 1)),
            //     "Length is wrong {}, it should be {}",
            //     pp.len(),
            //     (s.len() - (Self::prefix_str().len() - 1))
            // );
            Some(p)
        } else {
            None
        }
    }
}

impl<const N: usize, T> Id<T, N> {
    fn new(inner: [u8; N]) -> Self {
        Self {
            _type: PhantomData,
            inner,
        }
    }

    pub fn random() -> Self {
        let mut rng = rng();
        Self::new(rng.random())
    }

    pub fn random_rng<R: Rng>(rng: &mut R) -> Self {
        Self::new(rng.random())
    }
}

impl<'facet, T: Facet<'facet>> Display for Id<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let type_n = T::SHAPE.type_identifier;
        let a = Alphabet::Rfc4648HexLower { padding: false };
        let o = base32::encode(a, &self.inner);
        write!(f, "{type_n}/{o}")
    }
}

// impl<'facet, T: Facet<'facet>> TryFrom<&str> for Id<T> {
//     type Error = IdErrors;

//     fn try_from(value: &str) -> Result<Self, Self::Error> {
//         let typ = format!("{}/", T::SHAPE.type_identifier);
//         let prefix_p = value.starts_with(&typ);
//         if prefix_p
//             && let Some(inner_str) = value.strip_prefix(&typ)
//             && inner_str.len() == 24
//             && inner_str.chars().all(|c| c.is_ascii_hexdigit())
//         {
//             let mut inner = [0u8; 12];
//             for i in (1usize..24usize).step_by(2) {
//                 if (i % 2) != 0 {
//                     let s = &inner_str[i - 1..=i];
//                     let b = u8::from_str_radix(s, 16).expect("Shouldn't error");
//                     //info!("Byte: {b:0>2X}");
//                     inner[i / 2] = b;
//                 }
//             }

//             Ok(Self {
//                 _type: PhantomData,
//                 inner,
//             })
//         } else {
//             let m = format!("Failed to parse '{value}' as id");
//             warn!(m);
//             Err(m.into())
//         }
//     }
// }