use core::hash::Hash;
#[cfg(feature = "wa_proto")]
use core::slice::Iter;
use std::{fmt::Debug, marker::PhantomData, num::NonZeroU32};
use harsh::Harsh;
use rapira::{Rapira, RapiraError};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "ts-types")]
use specta::{DataType, PrimitiveType, Type};
#[cfg(feature = "ts-types")]
use typescript_type_def::{
type_expr::{Ident, NativeTypeInfo, TypeExpr, TypeInfo},
TypeDef,
};
#[cfg(feature = "wa_proto")]
use wa_proto::{Incoming, Outcoming};
use super::ArmourError;
use crate::{collections::Result, GetType, Typ};
#[cfg(feature = "std")]
const HARSH_ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyz1234567890";
#[cfg(feature = "std")]
const SEPARATORS: &[u8] = b"cfhistu";
#[cfg(feature = "std")]
pub fn create_harsh(salt: &str) -> Harsh {
Harsh::builder()
.salt(salt)
.length(2)
.alphabet(HARSH_ALPHABET)
.separators(SEPARATORS)
.build()
.unwrap()
}
pub trait HarshIdent {
#[cfg(feature = "std")]
fn get_hasher() -> &'static Harsh;
#[cfg(feature = "std")]
fn get_name() -> &'static str;
}
#[repr(transparent)]
pub struct Hashid<T>(NonZeroU32, PhantomData<T>);
impl<T> Hashid<T> {
#[inline]
pub fn get(self) -> u32 {
self.0.get()
}
#[inline]
pub fn get_non_zero_u32(self) -> NonZeroU32 {
self.0
}
#[inline]
pub fn new(id: NonZeroU32) -> Self {
Hashid(id, PhantomData)
}
#[inline]
pub fn new_unsafe(id: u32) -> Self {
let id = NonZeroU32::new(id).unwrap();
Hashid(id, PhantomData)
}
#[inline]
pub fn to_le_bytes(self) -> [u8; 4] {
self.0.get().to_le_bytes()
}
#[inline]
pub fn to_be_bytes(self) -> [u8; 4] {
self.0.get().to_be_bytes()
}
#[inline]
pub fn from_le_bytes(b: [u8; 4]) -> Self {
let id = u32::from_le_bytes(b);
Self::new_unsafe(id)
}
#[inline]
pub fn from_be_bytes(b: [u8; 4]) -> Self {
let id = u32::from_be_bytes(b);
Self::new_unsafe(id)
}
pub fn increment(self) -> Self {
Self::new_unsafe(self.get() + 1)
}
}
impl<T: HarshIdent> Hashid<T> {
#[cfg(feature = "std")]
#[inline]
pub fn get_name() -> &'static str {
T::get_name()
}
#[inline]
pub fn deser(s: &str) -> Result<Hashid<T>> {
let v = T::get_hasher()
.decode(s)
.map_err(|_| ArmourError::UnknownError)
.and_then(|vec| {
vec.first()
.map(|u| *u as u32)
.ok_or(ArmourError::UnknownError)
})?;
let v = NonZeroU32::new(v).ok_or(ArmourError::NonZeroError)?;
Ok(Self::new(v))
}
#[inline]
pub fn ser(self) -> String {
T::get_hasher().encode(&[self.get() as u64])
}
}
impl<T> Clone for Hashid<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Hashid<T> {}
impl<T> PartialOrd for Hashid<T> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T> Ord for Hashid<T> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<T> PartialEq for Hashid<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T> Eq for Hashid<T> {}
impl<T> TryFrom<u32> for Hashid<T> {
type Error = ArmourError;
fn try_from(val: u32) -> Result<Hashid<T>, Self::Error> {
Ok(Hashid::new(val.try_into()?))
}
}
impl<T> From<Hashid<T>> for u32 {
fn from(id: Hashid<T>) -> Self {
id.get()
}
}
impl<T: HarshIdent> std::fmt::Display for Hashid<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.ser())
}
}
impl<T> Debug for Hashid<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ID").field(&self.0).finish()
}
}
impl<T> Hash for Hashid<T> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
#[cfg(feature = "std")]
impl<T: HarshIdent> Serialize for Hashid<T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let s = self.ser();
serializer.serialize_str(&s)
}
}
#[cfg(feature = "std")]
impl<'de, T: HarshIdent> Deserialize<'de> for Hashid<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
let s: &str = Deserialize::deserialize(deserializer)?;
let a = Hashid::<T>::deser(s).map_err(|_| D::Error::custom("id value error"))?;
Ok(a)
}
}
#[cfg(feature = "bincode")]
impl<T> Encode for Hashid<T> {
fn encode<E: bincode::enc::Encoder>(
&self,
encoder: &mut E,
) -> core::result::Result<(), bincode::error::EncodeError> {
bincode::Encode::encode(&self.0, encoder)
}
}
#[cfg(feature = "bincode")]
impl<T> Decode for Hashid<T> {
fn decode<D: bincode::de::Decoder>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
let id: NonZeroU32 = bincode::de::Decode::decode(decoder)?;
Ok(Hashid::new(id))
}
}
impl<T> Rapira for Hashid<T> {
const STATIC_SIZE: Option<usize> = Some(4);
#[inline]
fn size(&self) -> usize {
4
}
#[inline]
fn from_slice(slice: &mut &[u8]) -> Result<Self, rapira::RapiraError>
where
Self: Sized,
{
let u = u32::from_slice(slice)?;
let id = NonZeroU32::new(u).ok_or(RapiraError::NonZeroError)?;
Ok(Hashid::<T>::new(id))
}
#[inline]
fn check_bytes(slice: &mut &[u8]) -> Result<(), rapira::RapiraError>
where
Self: Sized,
{
let bytes: &[u8] = slice.get(..4).ok_or(RapiraError::SliceLenError)?;
if bytes == [0, 0, 0, 0] {
return Err(RapiraError::NonZeroError);
}
*slice = unsafe { slice.get_unchecked(4..) };
Ok(())
}
#[inline]
fn from_slice_unchecked(slice: &mut &[u8]) -> Result<Self, rapira::RapiraError>
where
Self: Sized,
{
let u = u32::from_slice_unchecked(slice)?;
Ok(Hashid::<T>::new_unsafe(u))
}
#[inline]
unsafe fn from_slice_unsafe(slice: &mut &[u8]) -> Result<Self, rapira::RapiraError>
where
Self: Sized,
{
let u = u32::from_slice_unsafe(slice)?;
Ok(Hashid::<T>::new_unsafe(u))
}
#[inline]
fn convert_to_bytes(&self, slice: &mut [u8], cursor: &mut usize) {
self.get().convert_to_bytes(slice, cursor);
}
}
#[cfg(feature = "wa_proto")]
impl<T: HarshIdent> Incoming for Hashid<T> {
#[cfg(not(feature = "std"))]
fn init(args: &mut IterMut<u32>) -> Result<(u32, Self), ProtocolError>
where
Self: Sized,
{
let el: u32 = *args.next().ok_or(ProtocolError(ARGS_NEXT_ERROR))?;
let id = Hashid::<T>::new_unsafe(el);
Ok((id, el))
}
#[cfg(feature = "std")]
fn args(&self, args: &mut Vec<u32>) -> Result<(), wa_proto::ProtocolError> {
args.push(self.get());
Ok(())
}
fn fill(
&self,
_: &mut std::cell::RefMut<[u8]>,
args: &mut std::slice::Iter<u32>,
) -> Result<(), wa_proto::ProtocolError> {
args.next()
.ok_or_else(|| wa_proto::ProtocolError("args is end".to_owned()))?;
Ok(())
}
}
#[cfg(feature = "wa_proto")]
impl<T: HarshIdent> Outcoming for Hashid<T> {
#[cfg(not(feature = "std"))]
fn args(&self, args: &mut Vec<u32>) -> Result<(), ProtocolError> {
args.push(self.get());
Ok(())
}
#[cfg(feature = "std")]
fn read(_: &[u8], args: &mut Iter<u32>) -> Result<Self, wa_proto::ProtocolError> {
let u = *args
.next()
.ok_or_else(|| wa_proto::ProtocolError("args is end".to_owned()))?;
let id = Hashid::<T>::new_unsafe(u);
Ok(id)
}
}
impl<T> GetType for Hashid<T> {
const TYPE: Typ = Typ::U32;
}
#[cfg(feature = "ts-types")]
impl<T> Type for Hashid<T> {
const NAME: &'static str = "Hashid";
fn inline(_: specta::DefOpts, _: &[specta::DataType]) -> specta::DataType {
DataType::Primitive(PrimitiveType::u32)
}
}
#[cfg(feature = "ts-types")]
impl<T: 'static> TypeDef for Hashid<T> {
const INFO: TypeInfo = TypeInfo::Native(NativeTypeInfo {
r#ref: TypeExpr::ident(Ident("Hashid")),
});
}