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;
#[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())
}
}
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
}
fn total_number_ids() -> usize {
256 ^ Self::length()
}
}
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()))
}
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.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 = {
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(())
}
}