use core::num::NonZeroU32;
use std::ops::Range;
use rapira::FromU8;
use serde::{Serialize, Serializer, ser::SerializeStruct};
use zerocopy::{Immutable, IntoBytes, KnownLayout, TryFromBytes};
use crate::{
KeyScheme, KeyType, RecordStatus,
cid::Cid,
types::{ArmourError, Result, enc::IdHasher, ident::ID, num_ops::g14},
};
type FiveByte = [u8; 5];
#[derive(
Debug,
PartialOrd,
Ord,
PartialEq,
Eq,
Clone,
Copy,
IntoBytes,
TryFromBytes,
Immutable,
KnownLayout,
)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "ts-rs", ts(concrete(I = ())))]
#[cfg_attr(feature = "ts-rs", ts(export, export_to = "uid.ts"))]
#[repr(C)]
pub struct Uid<I, S = RecordStatus> {
pub status: S,
id: ID<I>,
}
impl<I, T> Uid<I, T> {
pub const ACTIVE_START: FiveByte = [RecordStatus::ACTIVE, 0, 0, 0, 0];
pub const DELETED_END: FiveByte = [RecordStatus::DELETED, 0, 0, 0, 0];
pub const NOT_DELETED_RANGE: Range<FiveByte> = Self::ACTIVE_START..Self::DELETED_END;
pub const NOT_DELETED_RANGE_SLICE: Range<&'static [u8; 5]> =
&Self::ACTIVE_START..&Self::DELETED_END;
#[inline]
pub fn new(id: ID<I>) -> Uid<I, T>
where
T: Default,
{
Uid {
status: Default::default(),
id,
}
}
#[inline]
pub fn with_status(id: ID<I>, status: T) -> Uid<I, T> {
Uid { status, id }
}
#[inline]
pub fn copy_with_status(mut self, status: T) -> Self {
self.status = status;
self
}
#[inline]
pub fn get_u32(&self) -> u32 {
self.id.get()
}
#[inline]
pub fn get_id(&self) -> ID<I> {
self.id
}
#[inline]
pub fn get_status(&self) -> T
where
T: Copy,
{
self.status
}
pub fn set_status(&mut self, status: T) {
self.status = status;
}
#[inline]
pub fn is_deleted(&self) -> bool
where
T: Copy,
u8: From<T>,
{
u8::from(self.status) == RecordStatus::DELETED
}
pub fn group_prefix(&self) -> [u8; 4]
where
Self: IntoBytes + Immutable,
{
let mut bytes = [0; 4];
bytes.copy_from_slice(&IntoBytes::as_bytes(self)[..4]);
bytes
}
}
impl<I, T> From<Uid<I, T>> for u32 {
fn from(val: Uid<I, T>) -> u32 {
val.get_u32()
}
}
impl<I, T> PartialEq<ID<I>> for Uid<I, T> {
fn eq(&self, other: &ID<I>) -> bool {
self.id.eq(other)
}
}
impl<I, T: Default> TryFrom<u32> for Uid<I, T> {
type Error = ArmourError;
fn try_from(id: u32) -> Result<Self> {
let id = NonZeroU32::new(id).ok_or(ArmourError::NonZeroError)?;
let id = ID::new(id);
Ok(Self::new(id))
}
}
impl<T: Serialize, I: IdHasher> Serialize for Uid<I, T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let id = self.id.ser();
let mut s = serializer.serialize_struct("Uid", 2)?;
s.serialize_field("status", &self.status)?;
s.serialize_field("id", &id)?;
s.end()
}
}
impl<I, T> Cid for Uid<I, T>
where
I: Ord,
T: Ord + Copy + FromU8 + TryFrom<u8>,
ArmourError: From<<T as TryFrom<u8>>::Error>,
u8: From<T>,
{
type B = FiveByte;
const TY: KeyScheme = KeyScheme::Typed(&[KeyType::U8, KeyType::U32]);
const GROUP_BITS: u32 = 32;
#[inline]
fn encode(&self) -> FiveByte {
let id_vec: [u8; 4] = self.id.to_le_bytes();
let status = u8::from(self.status);
[status, id_vec[3], id_vec[2], id_vec[1], id_vec[0]]
}
fn decode(bytes: &FiveByte) -> Result<Self> {
let first_byte = bytes[0];
let status: T = T::try_from(first_byte)?;
let id_bytes: [u8; 4] = [bytes[4], bytes[3], bytes[2], bytes[1]];
let id = NonZeroU32::new(u32::from_le_bytes(id_bytes)).ok_or(ArmourError::NonZeroError)?;
let id = ID::new(id);
Ok(Uid { status, id })
}
fn from_bytes(bytes: &[u8]) -> Result<Self> {
let bytes = <FiveByte>::try_from(bytes)?;
Self::decode(&bytes)
}
fn group_id(&self) -> u32 {
let status = u8::from(self.status);
let id = self.id.get();
g14(status, id)
}
}
#[cfg(feature = "ts")]
impl<I, T> TypeDef for Uid<I, T>
where
T: 'static + TypeDef,
I: 'static,
{
const INFO: TypeInfo = TypeInfo::Defined(DefinedTypeInfo {
def: TypeDefinition {
docs: None,
path: &[],
name: Ident("Uid"),
generic_vars: &[],
def: TypeExpr::Object(TypeObject {
docs: None,
index_signature: None,
fields: &[
ObjectField {
docs: None,
name: TypeString {
docs: None,
value: "id",
},
optional: false,
r#type: TypeExpr::Ref(&<ID<T>>::INFO),
},
ObjectField {
docs: None,
name: TypeString {
docs: None,
value: "status",
},
optional: false,
r#type: TypeExpr::Ref(&T::INFO),
},
],
}),
},
generic_args: &[],
});
}
#[cfg(feature = "ts")]
impl<T> Type for Id<T> {
const NAME: &'static str = "Id";
fn inline(opts: DefOpts, generics: &[DataType]) -> DataType {
DataType::Object(ObjectType {
name: "Id".to_owned(),
generics: Vec::new(),
fields: vec![
ObjectField {
name: "status".to_owned(),
ty: RecordStatus::inline(
DefOpts {
parent_inline: false,
type_map: opts.type_map,
},
generics,
),
optional: false,
flatten: false,
},
ObjectField {
name: "id".to_owned(),
ty: ID::<T>::inline(
DefOpts {
parent_inline: false,
type_map: opts.type_map,
},
generics,
),
optional: false,
flatten: false,
},
],
tag: None,
type_id: None,
})
}
}