use std::{
fmt::{Debug, Display},
hash::Hash,
marker::PhantomData,
str::FromStr,
};
use rand::Rng;
use serde::{Deserialize, Serialize};
use crate::FromStrVisitor;
pub struct Id128<K>([u8; 16], PhantomData<K>);
impl<K> Id128<K> {
pub const fn from_uint(val: u128) -> Self {
Self(val.to_be_bytes(), PhantomData)
}
pub const fn from_array(array: &[u8; 16]) -> Self {
Self(*array, PhantomData)
}
pub const fn to_bytes(self) -> [u8; 16] {
self.0
}
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
Some(Self(bytes.try_into().ok()?, PhantomData))
}
pub fn random() -> Self {
loop {
let id: u128 = rand::thread_rng().gen();
if id > u16::MAX as u128 {
return Self(id.to_be_bytes(), PhantomData);
}
}
}
pub const fn to_any(&self) -> AnyId {
Id128(self.0, PhantomData)
}
pub fn to_uint(&self) -> u128 {
u128::from_be_bytes(self.0)
}
}
impl<K> Clone for Id128<K> {
fn clone(&self) -> Self {
*self
}
}
impl<K> Copy for Id128<K> {}
impl<K> PartialEq for Id128<K> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<K> Eq for Id128<K> {}
impl<K> PartialOrd<Id128<K>> for Id128<K> {
fn partial_cmp(&self, other: &Id128<K>) -> Option<std::cmp::Ordering> {
Some(self.0.cmp(&other.0))
}
}
impl<K> Ord for Id128<K> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<K> Hash for Id128<K> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<K> From<[u8; 16]> for Id128<K> {
fn from(value: [u8; 16]) -> Self {
Self(value, PhantomData)
}
}
impl<K> From<&[u8; 16]> for Id128<K> {
fn from(value: &[u8; 16]) -> Self {
Self(*value, PhantomData)
}
}
impl<K> Debug for Id128<K> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hexhex::hex(&self.0))
}
}
impl<K> Display for Id128<K> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hexhex::hex(&self.0))
}
}
pub mod idkind {
pub trait IdKind {
fn name() -> &'static str;
}
pub struct Entity;
pub struct Object;
pub struct Any;
impl IdKind for Entity {
fn name() -> &'static str {
"entity id"
}
}
impl IdKind for Object {
fn name() -> &'static str {
"entity id"
}
}
impl IdKind for Any {
fn name() -> &'static str {
"any id"
}
}
}
pub type Eid = Id128<idkind::Entity>;
pub type ObjId = Id128<idkind::Object>;
pub type AnyId = Id128<idkind::Any>;
impl<K> FromStr for Id128<K> {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let hex = hexhex::decode(s).map_err(|_| "invalid format")?;
let array: [u8; 16] = hex.try_into().map_err(|_| "invalid length")?;
let min = 32768_u128.to_be_bytes();
if array != [0; 16] && array < min {
return Err("invalid value");
}
Ok(Id128(array, PhantomData))
}
}
impl<'de, K: idkind::IdKind> Deserialize<'de> for Id128<K> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_str(FromStrVisitor::new(K::name()))
}
}
impl<K> Serialize for Id128<K> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&hexhex::hex(&self.0).to_string())
}
}
#[test]
fn from_hex_literal() {
let _ = AnyId::from(hexhex::hex_literal!("1234abcd1234abcd1234abcd1234abcd"));
}