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>;
#[derive(Clone, Copy)]
#[repr(u32)]
pub enum BuiltinID {
Authly = 0,
PropEntity = 1,
PropAuthlyRole = 2,
AttrAuthlyRoleGetAccessToken = 3,
AttrAuthlyRoleAuthenticate = 4,
AttrAuthlyRoleApplyDocument = 5,
RelEntityMembership = 6,
PropUsername = 7,
PropEmail = 8,
PropPasswordHash = 9,
PropLabel = 10,
PropK8sServiceAccount = 11,
}
impl BuiltinID {
pub const fn to_obj_id(self) -> ObjId {
Id128((self as u128).to_be_bytes(), PhantomData)
}
pub const fn label(self) -> Option<&'static str> {
match self {
Self::Authly => None,
Self::PropEntity => Some("entity"),
Self::PropAuthlyRole => Some("authly:role"),
Self::AttrAuthlyRoleGetAccessToken => Some("get_access_token"),
Self::AttrAuthlyRoleAuthenticate => Some("authenticate"),
Self::AttrAuthlyRoleApplyDocument => Some("apply_document"),
Self::PropUsername => None,
Self::PropEmail => None,
Self::RelEntityMembership => None,
Self::PropPasswordHash => None,
Self::PropLabel => None,
Self::PropK8sServiceAccount => None,
}
}
pub const fn attributes(self) -> &'static [BuiltinID] {
match self {
Self::PropAuthlyRole => &[
Self::AttrAuthlyRoleGetAccessToken,
Self::AttrAuthlyRoleAuthenticate,
Self::AttrAuthlyRoleApplyDocument,
],
_ => &[],
}
}
}
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"));
}