use crate::errors::MagicTypeIdError;
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::str::FromStr;
use typeid_prefix::{TypeIdPrefix, ValidationError};
use typeid_suffix::prelude::*;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "instrument")]
use tracing::{debug, instrument, trace};
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct MagicTypeId {
prefix: TypeIdPrefix,
suffix: TypeIdSuffix,
string_repr: String,
}
impl Ord for MagicTypeId {
fn cmp(&self, other: &Self) -> Ordering {
match self.suffix.cmp(&other.suffix) {
Ordering::Equal => self.prefix.cmp(&other.prefix),
other => other,
}
}
}
impl PartialOrd for MagicTypeId {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialOrd<str> for MagicTypeId {
fn partial_cmp(&self, other: &str) -> Option<Ordering> {
self.string_repr.as_str().partial_cmp(other)
}
}
impl PartialOrd<MagicTypeId> for str {
fn partial_cmp(&self, other: &MagicTypeId) -> Option<Ordering> {
self.partial_cmp(other.string_repr.as_str())
}
}
impl PartialOrd<String> for MagicTypeId {
fn partial_cmp(&self, other: &String) -> Option<Ordering> {
self.string_repr.partial_cmp(other)
}
}
impl PartialOrd<MagicTypeId> for String {
fn partial_cmp(&self, other: &MagicTypeId) -> Option<Ordering> {
self.partial_cmp(&other.string_repr)
}
}
impl Hash for MagicTypeId {
fn hash<H: Hasher>(&self, state: &mut H) {
self.string_repr.hash(state);
}
}
impl PartialEq<String> for MagicTypeId {
fn eq(&self, other: &String) -> bool {
&self.string_repr == other
}
}
impl PartialEq<MagicTypeId> for String {
fn eq(&self, other: &MagicTypeId) -> bool {
self == &other.string_repr
}
}
impl MagicTypeId {
#[must_use]
#[cfg_attr(feature = "instrument", instrument(level = "debug", skip(prefix, suffix), fields(prefix = %prefix, suffix = %suffix)))]
pub fn new(prefix: TypeIdPrefix, suffix: TypeIdSuffix) -> Self {
let string_repr = if prefix.is_empty() {
#[cfg(feature = "instrument")]
trace!("Creating MagicTypeId with empty prefix");
suffix.to_string()
} else {
#[cfg(feature = "instrument")]
trace!("Creating MagicTypeId with prefix and suffix");
format!("{prefix}_{suffix}")
};
#[cfg(feature = "instrument")]
debug!("Created MagicTypeId: {}", string_repr);
Self {
prefix,
suffix,
string_repr,
}
}
#[must_use]
pub const fn prefix(&self) -> &TypeIdPrefix {
&self.prefix
}
#[must_use]
pub const fn suffix(&self) -> &TypeIdSuffix {
&self.suffix
}
#[must_use]
pub const fn as_str(&self) -> &str {
self.string_repr.as_str()
}
}
impl Display for MagicTypeId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.string_repr)
}
}
impl FromStr for MagicTypeId {
type Err = MagicTypeIdError;
#[cfg_attr(feature = "instrument", instrument(level = "debug", fields(input = %s)))]
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some((prefix_str, suffix_str)) = s.rsplit_once('_') {
#[cfg(feature = "instrument")]
trace!(
"Parsing MagicTypeId with prefix '{}' and suffix '{}'",
prefix_str,
suffix_str
);
if prefix_str.is_empty() {
#[cfg(feature = "instrument")]
debug!("Empty prefix found, returning error");
return Err(MagicTypeIdError::Prefix(
ValidationError::InvalidStartCharacter,
));
}
let prefix = TypeIdPrefix::from_str(prefix_str)?;
let suffix = TypeIdSuffix::from_str(suffix_str)?;
#[cfg(feature = "instrument")]
debug!("Successfully parsed MagicTypeId with prefix and suffix");
Ok(Self::new(prefix, suffix))
} else {
#[cfg(feature = "instrument")]
trace!("Parsing MagicTypeId with no prefix, only suffix '{}'", s);
let suffix = TypeIdSuffix::from_str(s)?;
#[cfg(feature = "instrument")]
debug!("Successfully parsed MagicTypeId with no prefix");
Ok(Self::new(TypeIdPrefix::default(), suffix))
}
}
}
impl Deref for MagicTypeId {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.string_repr
}
}
impl AsRef<str> for MagicTypeId {
fn as_ref(&self) -> &str {
&self.string_repr
}
}
impl Borrow<str> for MagicTypeId {
fn borrow(&self) -> &str {
&self.string_repr
}
}
impl PartialEq<str> for MagicTypeId {
fn eq(&self, other: &str) -> bool {
self.string_repr == other
}
}
impl PartialEq<MagicTypeId> for str {
fn eq(&self, other: &MagicTypeId) -> bool {
self == other.string_repr
}
}
impl PartialEq<&str> for MagicTypeId {
fn eq(&self, other: &&str) -> bool {
&self.string_repr == other
}
}
impl PartialEq<MagicTypeId> for &str {
fn eq(&self, other: &MagicTypeId) -> bool {
*self == other.string_repr
}
}
#[cfg(feature = "serde")]
impl Serialize for MagicTypeId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.string_repr)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for MagicTypeId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::from_str(&s).map_err(serde::de::Error::custom)
}
}