idcator 0.1.1

A library for a typed id
Documentation
use std::{collections::HashSet, fmt::Display, hash::{Hash, Hasher}, marker::PhantomData, time::Duration};

use rand::{distributions::Standard, thread_rng, Rng};
use serde::{Deserialize, Serialize};
use thiserror::Error;

// pub trait Space {
//     fn indicator_bytes(&self) -> u8 {
//         0
//     }

//     fn length(&self) -> usize {
//         16
//     }

//     fn parse(&self, bytes: Vec<u8>) -> Result<Id<Self>, Errors>
//     where
//         Self: Sized,
//     {
//         let len = bytes.len();
//         if len == self.length() && self.has_suffix(&bytes) {
//             Ok(Id::new(bytes))
//         } else if len == self.length() {
//             Err(Errors::new("missing-suffix", ""))
//         } else {
//             Err(Errors::new("length", ""))
//         }
//     }

//     fn shrink_pad(&self, bytes: Vec<u8>) -> Vec<u8> {
//         if bytes.len() >= self.length() {
//             bytes[0..bytes.len()].to_vec()
//         } else if bytes.len() < self.length() {
//             let mut out = vec![0; self.length()];
//             out[0..bytes.len()].copy_from_slice(&bytes[0..bytes.len()]);
//             out
//         } else {
//             panic!("Empty bytes")
//         }
//     }

//     fn has_suffix(&self, bytes: &[u8]) -> bool {
//         bytes[bytes.len()] == self.indicator_bytes()
//     }

//     fn generate(&self) -> Id<Self>
//     where
//         Self: Sized,
//     {
//         let mut rng = thread_rng();
//         let out: Vec<u8> = (0..self.length()).map(|_| rng.gen()).collect();
//         //out.push(Self::INDICATOR_BYTE);
//         Id::new(out)
//     }
// }

// #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
// pub struct Id<S: Space + ?Sized> {
//     inner: Vec<u8>,
//     _marker: PhantomData<S>,
// }

// impl<S: Space> Id<S> {
//     fn new(bytes: Vec<u8>) -> Self {
//         Self {
//             inner: bytes,
//             _marker: PhantomData,
//         }
//     }

//     pub fn create(space: S, bytes: Vec<u8>) -> Result<Self, Errors> {
//         if bytes.len() == space.length() + 1 {
//             Ok(Self::new(bytes))
//         } else {
//             Err(Errors::new("size", "Too long"))
//         }
//     }

//     #[allow(dead_code)]
//     fn push(&mut self, _item: u8) {
//         unimplemented!("Block push directly to id")
//     }
// }

// impl<S: Space> Display for Id<S> {
//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
//         let mut out = String::new(); //format!("{:0>2x}", self.);
//         for byte in self.inner.iter() {
//             let h = format!("{byte:0>2x}");
//             out.push_str(&h);
//         }
//         write!(f, "{out}")
//     }
// }

// fn byte_suffix(bytes: &[u8]) -> u8 {
//     bytes[bytes.len()]
// }

// New

#[derive(Debug, Error)]
pub enum Errors {
	#[error("")]
	NotOnlyHexadecimal,
	#[error("")]
	TooShort(usize),
	#[error("")]
	TooLong(usize),
    #[error("Missing <{0}> byte from byte-array")]
    MissingIndicatorByte(u8),
    #[error("{0}")]
    Custom(String),
}

impl Errors {
    pub fn new<S: AsRef<str>>(mesg: S) -> Self {
        Self::Custom(mesg.as_ref().to_string())
    }
}

///TODO: Better, more specific name
pub trait Space {
    fn indicator() -> u8;

    fn indicator_char() -> char {
        char::from(Self::indicator())
    }

    fn length() -> usize {
        16
    }

	fn str_length() -> usize {
		Self::length() * 2
	}
	
	/// Don't know if this works
    fn total_number_ids() -> usize {
        256 ^ Self::length()
    }
}

/// Idea is to use this to estimate a good length for an id-space
pub fn napkin_math(interval: Duration, per: u64) -> usize {
    let mut length: usize = 2;
    while true {}
    todo!()
}

#[cfg(feature = "serde_feature")]
#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Hash)]
pub struct Id<S: Space> {
    indicator: u8,
    bytes: Vec<u8>,
    _marker: PhantomData<S>,
}

impl<S: Space> Id<S> {
    fn new(bytes: Vec<u8>) -> Id<S> {
        Self {
            indicator: S::indicator(),
            bytes,
            _marker: PhantomData,
        }
    }

    pub fn create(bytes: Vec<u8>) -> Result<Self, Errors> {
        if bytes.len() != S::length() + 1 {
            return Err(Errors::new("invalid-length"));
        };

        if bytes[0] != S::indicator() {
            return Err(Errors::new("incorrect-indicator"));
        };

        Ok(Self::new(bytes[1..=S::length()].to_vec()))
    }

    /// Should be checked at compile time, so always true
    pub fn same_space(&self, _other: Id<S>) -> bool {
        true
    }

    pub fn random() -> Self {
        let randomized = thread_rng()
            .sample_iter(Standard)
            .take(S::length())
            .collect();
        Self::new(randomized)
    }
}

impl<S: Space> Display for Id<S> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut out = format!("{:0>2x}", self.indicator);
        for byte in self.bytes.iter() {
            let st = format!("{:0>2x}", byte);
            out.push_str(&st);
        }
        write!(f, "{out}")
    }
}

impl<S: Space> PartialEq<Vec<u8>> for Id<S> {
    fn eq(&self, other: &Vec<u8>) -> bool {
        if self.indicator == other[0] {
            self.bytes == other[1..S::length()]
        } else {
            false
        }
    }
}

impl<S: Space> TryFrom<&[u8]> for Id<S> {
    type Error = Errors;

    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        Self::create(value.to_vec())
    }
}

impl<S: Space> TryFrom<&str> for Id<S> {
    type Error = Errors;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        // if !value.starts_with(S::indicator_char()) {
        //     return Err(Errors::MissingIndicatorByte(S::indicator()));
        // };

        // if !value.len() == S::length() {
        //     return Err(Errors::new("Wrong length"));
        // };
		
        if !value.chars().all(|x| x.is_ascii_hexdigit()) {
            return Err(Errors::new("Not all hexadecimal"));
        };

		let bytes = value.as_bytes();
		Id::create(bytes.to_vec())
    }
}

pub struct TestingSpace;

impl Space for TestingSpace {
    fn length() -> usize {
        20
    }

    fn indicator() -> u8 {
        0
    }
}

pub type TestingId = Id<TestingSpace>;

#[cfg(feature = "serde_feature")]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Ids<S: Space + Hash + Eq>(HashSet<Id<S>>);

impl<S: Space + Hash + Eq> Ids<S> {
    pub fn len(&self) -> usize {
        self.0.len()
    }
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
}

impl<S: Space + Hash + Eq> PartialEq for Ids<S> {
    fn eq(&self, other: &Self) -> bool {
        if self.len() != other.len() {
            return false;
        };

        let zipped: Vec<(&Id<S>, &Id<S>)> = self.0.iter().zip(other.0.iter()).collect();

        todo!()
    }
}

#[cfg(test)]
mod tests {
	use rand::distributions::Alphanumeric;

use super::*;

    #[test]
    fn gen_rand_id() -> Result<(), Errors> {
		for _ in 0..20 {
			let mut out = vec![TestingSpace::indicator()];
			let mut bytes: Vec<u8> = thread_rng().sample_iter(Standard).take(TestingSpace::length()).collect();
			
			out.append(&mut bytes);
			let strng = {
				// let mut o = String::new();
				// for b in out.iter() {
				// 	let s = format!("{b:0>2x}");
				// 	o.push_str(&s);
				// }
				// o
				out.iter().fold("".to_string(), |x,y| format!("{x}{y:0>2x}"))
			};
			let rand_t_id: Id<TestingSpace> = Id::create(out.clone())?;
			eprintln!("id: {}", rand_t_id);
			assert!(rand_t_id.to_string() == strng, "String representations do not match");
			assert!(rand_t_id.to_string().len() == TestingSpace::str_length() + 2, "Wrong string lengths")
		}
		Ok(())
	}

}