use core::{hash::Hash, str::FromStr};
use std::{fmt::Debug, marker::PhantomData, num::NonZeroU32};
use rapira::{Rapira, RapiraError};
use rend::NonZeroU32_be;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use zerocopy::{Immutable, IntoBytes, KnownLayout, TryFromBytes};
use super::{ArmourError, IdStr, enc::IdHasher, num_ops::g4bits};
use crate::{Cid, GetType, KeyScheme, KeyType, Typ, cid::SEQ_BITS, types::Result};
#[repr(transparent)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[derive(IntoBytes, FromBytes, Immutable, KnownLayout)]
pub struct ID<T>(pub NonZeroU32_be, PhantomData<T>);
impl<T> Clone for ID<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for ID<T> {}
impl<T> PartialOrd for ID<T> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for ID<T> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<T> PartialEq for ID<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T> Eq for ID<T> {}
impl<T> PartialEq<ID<T>> for Option<ID<T>> {
fn eq(&self, other: &ID<T>) -> bool {
match self {
Some(id) => id == other,
None => false,
}
}
}
impl<T> PartialEq<Option<ID<T>>> for ID<T> {
fn eq(&self, other: &Option<ID<T>>) -> bool {
match other {
Some(id) => id == self,
None => false,
}
}
}
impl<T> ID<T> {
#[inline]
pub fn get(self) -> u32 {
self.0.get()
}
#[inline]
pub fn get_non_zero(self) -> NonZeroU32 {
self.0.to_native()
}
#[inline]
pub fn new(id: NonZeroU32) -> Self {
ID(NonZeroU32_be::from_native(id), PhantomData)
}
#[inline]
pub fn from_u32_le(id: u32) -> Result<Self> {
let id = NonZeroU32::new(id).ok_or(ArmourError::NonZeroError)?;
Ok(Self::new(id))
}
#[inline]
pub fn to_le_bytes(self) -> [u8; 4] {
self.0.get().to_le_bytes()
}
#[inline]
pub fn to_be_bytes(self) -> [u8; 4] {
zerocopy::transmute!(self)
}
#[inline]
pub fn from_be_bytes(bytes: [u8; 4]) -> Result<Self> {
zerocopy::try_transmute!(bytes).map_err(|_| ArmourError::NonZeroError)
}
#[inline]
pub fn increment(self) -> Self {
let id = self.0.to_native().saturating_add(1);
Self::new(id)
}
}
impl<T: IdHasher> ID<T> {
#[inline]
pub fn deser(s: &str) -> Result<ID<T>> {
let v = T::deser(s)? as u32;
zerocopy::try_transmute!(v).map_err(|_| ArmourError::NonZeroError)
}
#[inline]
pub fn ser(self) -> IdStr {
let u: u32 = zerocopy::transmute!(self);
T::ser(u as u64)
}
}
impl<T> TryFrom<u32> for ID<T> {
type Error = ArmourError;
fn try_from(val: u32) -> Result<ID<T>, Self::Error> {
Ok(ID::new(val.try_into()?))
}
}
impl<T: IdHasher> TryFrom<&str> for ID<T> {
type Error = ArmourError;
fn try_from(val: &str) -> Result<ID<T>, Self::Error> {
Self::deser(val)
}
}
impl<T: IdHasher> FromStr for ID<T> {
type Err = ArmourError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::deser(s)
}
}
impl<T> From<ID<T>> for u32 {
#[inline(always)]
fn from(id: ID<T>) -> Self {
id.get()
}
}
impl<T: IdHasher> std::fmt::Display for ID<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.ser())
}
}
impl<T> Debug for ID<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ID").field(&self.0).finish()
}
}
impl<T> Hash for ID<T> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
#[cfg(feature = "std")]
impl<T: IdHasher> Serialize for ID<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: IdHasher> Deserialize<'de> for ID<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 = ID::<T>::deser(s).map_err(|_| D::Error::custom("id value error"))?;
Ok(a)
}
}
impl<T> Rapira for ID<T> {
const STATIC_SIZE: Option<usize> = Some(4);
const MIN_SIZE: usize = 4;
#[inline]
fn size(&self) -> usize {
4
}
#[inline]
fn check_bytes(slice: &mut &[u8]) -> Result<(), rapira::RapiraError>
where
Self: Sized,
{
let bytes: &[u8] = slice.get(..4).ok_or(RapiraError::SliceLen)?;
if bytes == [0u8; 4] {
return Err(RapiraError::NonZero);
}
*slice = unsafe { slice.get_unchecked(4..) };
Ok(())
}
#[inline]
fn from_slice(slice: &mut &[u8]) -> Result<Self, rapira::RapiraError>
where
Self: Sized,
{
let bytes = <[u8; 4]>::from_slice(slice)?;
let id = Self::decode_owned(bytes).map_err(|_| RapiraError::NonZero)?;
Ok(id)
}
#[inline]
fn convert_to_bytes(&self, slice: &mut [u8], cursor: &mut usize) {
let bytes: &[u8; 4] = zerocopy::transmute_ref!(self);
bytes.convert_to_bytes(slice, cursor);
}
}
impl<T> GetType for ID<T> {
const TYPE: Typ = Typ::Ident;
}
impl<T> Cid for ID<T> {
type B = [u8; 4];
const TY: KeyScheme = KeyScheme::Typed(&[KeyType::U32]);
const GROUP_BITS: u32 = SEQ_BITS;
#[inline]
fn encode(&self) -> Self::B {
zerocopy::transmute!(*self)
}
#[inline]
fn encode_owned(self) -> Self::B {
zerocopy::transmute!(self)
}
#[inline]
fn from_bytes(bytes: &[u8]) -> Result<Self> {
let bytes: &[u8; 4] = bytes
.get(..4)
.ok_or(ArmourError::BytesLenError)?
.try_into()?;
Self::decode(bytes)
}
#[inline]
fn decode(bytes: &Self::B) -> Result<Self> {
zerocopy::try_transmute!(*bytes).map_err(|_| ArmourError::NonZeroError)
}
#[inline]
fn decode_owned(bytes: Self::B) -> Result<Self> {
zerocopy::try_transmute!(bytes).map_err(|_| ArmourError::NonZeroError)
}
#[inline]
fn group_id(&self) -> u32 {
let id = self.get();
g4bits(id, Self::GROUP_BITS)
}
}
#[cfg(feature = "ts-rs")]
impl<T> ts_rs::TS for ID<T> {
type WithoutGenerics = ID<()>;
type OptionInnerType = Self;
fn name() -> String {
"ID".to_owned()
}
fn decl_concrete() -> String {
format!("type {} = {};", Self::name(), Self::inline())
}
fn decl() -> String {
let inline = <ID<()> as ::ts_rs::TS>::inline();
format!("type {} = {};", Self::name(), inline)
}
fn inline() -> String {
"string".to_owned()
}
fn inline_flattened() -> String {
panic!("{} cannot be flattened", Self::name())
}
fn output_path() -> Option<std::path::PathBuf> {
Some(std::path::PathBuf::from("ID.ts"))
}
}
#[cfg(feature = "ts")]
impl<T: 'static> TypeDef for ID<T> {
const INFO: TypeInfo = TypeInfo::Native(NativeTypeInfo {
r#ref: TypeExpr::ident(Ident("ID")),
});
}