use std::borrow::Borrow;
use std::convert::TryFrom;
use std::fmt::{self, Display};
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::str::FromStr;
use std::sync::OnceLock;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use ulid::Ulid;
use uuid::Uuid;
use crate::error::{EntityIdError, IdentifierError};
use crate::identifier::{Identifier, UlidIdentifier, UuidIdentifier};
pub trait Prefix {
fn prefix() -> &'static str;
fn delimiter() -> &'static str {
"_" }
}
#[derive(Debug)]
pub struct EntityId<T: Prefix, I: Identifier> {
id: I,
cached_str: OnceLock<String>, _marker: PhantomData<T>,
}
impl<T: Prefix, I: Identifier> EntityId<T, I> {
pub fn new<S: AsRef<str>>(s: S) -> Result<Self, EntityIdError> {
let s = s.as_ref();
let expected_prefix = format!("{}{}", T::prefix(), T::delimiter());
if !s.starts_with(&expected_prefix) {
return Err(EntityIdError::InvalidFormat);
}
let id_part = &s[expected_prefix.len()..];
let id = I::parse(id_part).map_err(|_| EntityIdError::InvalidIdentifier)?;
Ok(Self {
id,
cached_str: OnceLock::new(),
_marker: PhantomData,
})
}
pub fn generate() -> Self {
Self {
id: I::generate(),
cached_str: OnceLock::new(),
_marker: PhantomData,
}
}
pub fn from_identifier(id: I) -> Self {
Self {
id,
cached_str: OnceLock::new(),
_marker: PhantomData,
}
}
pub fn as_str(&self) -> &str {
self.cached_str.get_or_init(|| {
let mut s = String::with_capacity(T::prefix().len() + T::delimiter().len() + 36);
s.push_str(T::prefix());
s.push_str(T::delimiter());
s.push_str(self.id.as_str());
s
})
}
pub fn id_str(&self) -> &str {
self.id.as_str()
}
pub fn timestamp_ms(&self) -> Option<u64> {
self.id.timestamp_ms()
}
pub fn identifier(&self) -> &I {
&self.id
}
pub fn prefix() -> &'static str {
T::prefix()
}
pub fn delimiter() -> &'static str {
T::delimiter()
}
pub fn builder() -> EntityIdBuilder<T, I> {
EntityIdBuilder::new()
}
pub fn from_raw_str<S: AsRef<str>>(s: S) -> Result<Self, EntityIdError> {
let id = I::parse(s).map_err(|_| EntityIdError::InvalidIdentifier)?;
Ok(Self::from_identifier(id))
}
pub fn parse_raw_str<S, E, F>(s: S, error_mapper: F) -> Result<Self, E>
where
S: AsRef<str>,
F: FnOnce(IdentifierError) -> E,
{
let id = I::parse(s).map_err(error_mapper)?;
Ok(Self::from_identifier(id))
}
pub fn to_raw_string(&self) -> String {
self.id_str().to_string()
}
pub fn to_identifier(&self) -> I {
self.id.clone()
}
}
impl<T: Prefix, I: Identifier> Hash for EntityId<T, I> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl<T: Prefix, I: Identifier> PartialEq for EntityId<T, I> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl<T: Prefix, I: Identifier> Eq for EntityId<T, I> {}
impl<T: Prefix, I: Identifier> Clone for EntityId<T, I> {
fn clone(&self) -> Self {
Self {
id: self.id.clone(),
cached_str: OnceLock::new(), _marker: PhantomData,
}
}
}
impl<T: Prefix, I: Identifier + Ord> PartialOrd for EntityId<T, I> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T: Prefix, I: Identifier + Ord> Ord for EntityId<T, I> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.id.cmp(&other.id)
}
}
impl<T: Prefix, I: Identifier> Serialize for EntityId<T, I> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de, T: Prefix, I: Identifier> Deserialize<'de> for EntityId<T, I> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::new(&s).map_err(serde::de::Error::custom)
}
}
impl<T: Prefix, I: Identifier> Display for EntityId<T, I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl<T: Prefix, I: Identifier> From<EntityId<T, I>> for String {
fn from(entity_id: EntityId<T, I>) -> Self {
entity_id.as_str().to_string()
}
}
impl<T: Prefix, I: Identifier> From<&EntityId<T, I>> for String {
fn from(entity_id: &EntityId<T, I>) -> Self {
entity_id.as_str().to_string()
}
}
impl<T: Prefix, I: Identifier> From<I> for EntityId<T, I> {
fn from(id: I) -> Self {
Self::from_identifier(id)
}
}
impl<T: Prefix> From<EntityId<T, UuidIdentifier>> for UuidIdentifier {
fn from(entity_id: EntityId<T, UuidIdentifier>) -> Self {
entity_id.id
}
}
impl<T: Prefix> From<&EntityId<T, UuidIdentifier>> for UuidIdentifier {
fn from(entity_id: &EntityId<T, UuidIdentifier>) -> Self {
entity_id.id
}
}
impl<T: Prefix> From<EntityId<T, UlidIdentifier>> for UlidIdentifier {
fn from(entity_id: EntityId<T, UlidIdentifier>) -> Self {
entity_id.id
}
}
impl<T: Prefix> From<&EntityId<T, UlidIdentifier>> for UlidIdentifier {
fn from(entity_id: &EntityId<T, UlidIdentifier>) -> Self {
entity_id.id
}
}
impl<T: Prefix, I: Identifier> TryFrom<&str> for EntityId<T, I> {
type Error = EntityIdError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
Self::new(s)
}
}
impl<T: Prefix, I: Identifier> TryFrom<String> for EntityId<T, I> {
type Error = EntityIdError;
fn try_from(s: String) -> Result<Self, Self::Error> {
Self::new(s)
}
}
impl<T: Prefix, I: Identifier> TryFrom<&String> for EntityId<T, I> {
type Error = EntityIdError;
fn try_from(s: &String) -> Result<Self, Self::Error> {
Self::new(s)
}
}
impl<T: Prefix, I: Identifier> FromStr for EntityId<T, I> {
type Err = EntityIdError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::new(s)
}
}
pub type UuidEntityId<T> = EntityId<T, UuidIdentifier>;
pub type UlidEntityId<T> = EntityId<T, UlidIdentifier>;
impl<T: Prefix> EntityId<T, UuidIdentifier> {
pub fn with_uuid(uuid: Uuid) -> Self {
Self::from_identifier(UuidIdentifier::from(uuid))
}
pub fn new_v4() -> Self {
Self::from_identifier(UuidIdentifier::new_v4())
}
pub fn new_v5(namespace: &Uuid, name: &str) -> Self {
Self::from_identifier(UuidIdentifier::new_v5(namespace, name))
}
}
impl<T: Prefix> EntityId<T, UlidIdentifier> {
pub fn with_ulid(ulid: Ulid) -> Self {
Self::from_identifier(UlidIdentifier::from(ulid))
}
pub fn with_timestamp(timestamp_ms: u64) -> Self {
Self::from_identifier(UlidIdentifier::with_timestamp(timestamp_ms))
}
pub fn monotonic_from(previous: Option<&Self>) -> Self {
let prev_id = previous.map(|p| p.identifier());
Self::from_identifier(UlidIdentifier::monotonic_from(prev_id))
}
}
pub struct EntityIdBuilder<T: Prefix, I: Identifier> {
id: Option<I>,
_marker: PhantomData<T>,
}
impl<T: Prefix, I: Identifier> Default for EntityIdBuilder<T, I> {
fn default() -> Self {
Self {
id: None,
_marker: PhantomData,
}
}
}
impl<T: Prefix, I: Identifier> EntityIdBuilder<T, I> {
pub fn new() -> Self {
Self::default()
}
pub fn with_identifier(mut self, id: I) -> Self {
self.id = Some(id);
self
}
pub fn build(self) -> EntityId<T, I> {
match self.id {
Some(id) => EntityId::from_identifier(id),
None => EntityId::generate(),
}
}
}
impl<T: Prefix> EntityIdBuilder<T, UuidIdentifier> {
pub fn with_uuid(mut self, uuid: Uuid) -> Self {
self.id = Some(UuidIdentifier::from(uuid));
self
}
pub fn with_uuid_v4(mut self) -> Self {
self.id = Some(UuidIdentifier::new_v4());
self
}
pub fn with_uuid_v5(mut self, namespace: &Uuid, name: &str) -> Self {
self.id = Some(UuidIdentifier::new_v5(namespace, name));
self
}
}
impl<T: Prefix> EntityIdBuilder<T, UlidIdentifier> {
pub fn with_ulid(mut self, ulid: Ulid) -> Self {
self.id = Some(UlidIdentifier::from(ulid));
self
}
pub fn with_timestamp(mut self, timestamp_ms: u64) -> Self {
self.id = Some(UlidIdentifier::with_timestamp(timestamp_ms));
self
}
pub fn with_monotonic_from(mut self, previous: Option<&EntityId<T, UlidIdentifier>>) -> Self {
let prev_id = previous.map(|p| p.identifier());
self.id = Some(UlidIdentifier::monotonic_from(prev_id));
self
}
}
impl<T: Prefix, I: Identifier> Borrow<str> for EntityId<T, I> {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl<T: Prefix, I: Identifier> AsRef<str> for EntityId<T, I> {
fn as_ref(&self) -> &str {
self.as_str()
}
}