use std::any::TypeId;
use std::fmt;
use std::num::NonZeroU64;
use std::sync::Arc;
use super::{IdentityError, IdentityKind};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RuntimeId(NonZeroU64);
impl RuntimeId {
#[allow(dead_code)]
pub(crate) const fn from_nonzero(value: NonZeroU64) -> Self {
Self(value)
}
#[allow(dead_code)]
pub(crate) const fn get(self) -> NonZeroU64 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RuntimeEpoch(NonZeroU64);
impl RuntimeEpoch {
#[allow(dead_code)]
pub(crate) const fn one() -> Self {
Self(NonZeroU64::MIN)
}
#[allow(dead_code)]
pub(crate) const fn from_nonzero(value: NonZeroU64) -> Self {
Self(value)
}
#[allow(dead_code)]
pub(crate) const fn get(self) -> NonZeroU64 {
self.0
}
#[allow(dead_code)]
pub(crate) fn checked_next(self) -> Option<Self> {
self.0
.get()
.checked_add(1)
.and_then(NonZeroU64::new)
.map(Self)
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct EngineId(Arc<str>);
impl EngineId {
pub fn new(value: impl Into<Arc<str>>) -> Result<Self, IdentityError> {
validate_identifier(value.into(), IdentityKind::Engine).map(Self)
}
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ProviderId(Arc<str>);
impl ProviderId {
pub fn new(value: impl Into<Arc<str>>) -> Result<Self, IdentityError> {
validate_identifier(value.into(), IdentityKind::Provider).map(Self)
}
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ProviderDeviceIdentity {
provider_id: ProviderId,
target_identity: Arc<str>,
}
impl ProviderDeviceIdentity {
pub fn new(
provider_id: ProviderId,
target_identity: impl Into<Arc<str>>,
) -> Result<Self, IdentityError> {
let target_identity = target_identity.into();
if target_identity.is_empty()
|| !target_identity
.chars()
.all(|character| character.is_ascii_graphic())
{
return Err(IdentityError::malformed(IdentityKind::ProviderTarget));
}
Ok(Self {
provider_id,
target_identity,
})
}
pub fn provider_id(&self) -> &ProviderId {
&self.provider_id
}
pub fn target_identity(&self) -> &str {
&self.target_identity
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct HardwareClassId(Arc<str>);
impl HardwareClassId {
pub fn new(value: impl Into<Arc<str>>) -> Result<Self, IdentityError> {
validate_identifier(value.into(), IdentityKind::HardwareClass).map(Self)
}
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RegistrationIdentity {
issuer: NonZeroU64,
ordinal: NonZeroU64,
}
impl RegistrationIdentity {
#[allow(dead_code)]
pub(crate) const fn new(issuer: NonZeroU64, ordinal: NonZeroU64) -> Self {
Self { issuer, ordinal }
}
pub fn ordinal(self) -> NonZeroU64 {
self.ordinal
}
}
impl fmt::Debug for RegistrationIdentity {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("RegistrationIdentity")
.field("ordinal", &self.ordinal)
.finish()
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ExecutionContextIdentity {
type_id: TypeId,
type_name: &'static str,
}
impl ExecutionContextIdentity {
pub fn of<T: 'static>() -> Self {
Self {
type_id: TypeId::of::<T>(),
type_name: std::any::type_name::<T>(),
}
}
pub fn type_name(&self) -> &'static str {
self.type_name
}
}
pub(super) fn validate_identifier(
value: Arc<str>,
kind: IdentityKind,
) -> Result<Arc<str>, IdentityError> {
let valid = value.is_ascii()
&& value.split('.').count() >= 2
&& value.split('.').all(valid_identifier_component);
valid
.then_some(value)
.ok_or_else(|| IdentityError::malformed(kind))
}
fn valid_identifier_component(component: &str) -> bool {
let bytes = component.as_bytes();
match bytes {
[single] => single.is_ascii_lowercase() || single.is_ascii_digit(),
[first, middle @ .., last] => {
(first.is_ascii_lowercase() || first.is_ascii_digit())
&& (last.is_ascii_lowercase() || last.is_ascii_digit())
&& middle.iter().all(|byte| {
byte.is_ascii_lowercase()
|| byte.is_ascii_digit()
|| matches!(byte, b'-' | b'_')
})
}
[] => false,
}
}