use std::hash::Hash;
use std::sync::Arc;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use crate::error::ErrorKind;
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
pub struct Identifier(Arc<str>);
impl Identifier {
pub fn new(string: &str) -> Result<Self, ErrorKind> {
if is_valid_identifier(string) {
Ok(Identifier(string.into()))
} else {
Err(ErrorKind::BadIdentifier)
}
}
#[cfg(test)]
pub(crate) fn new_raw(string: &str) -> Self {
assert!(is_valid_identifier(string));
Self(string.into())
}
pub fn from_uuidv4() -> Self {
Self::new(uuid::Uuid::new_v4().to_string().as_ref()).unwrap()
}
pub fn as_str(&self) -> &str {
self.as_ref()
}
}
fn is_valid_identifier(s: &str) -> bool {
s.len() <= 100 && s.bytes().all(|b| (0x20..=0x7E).contains(&b))
}
impl AsRef<str> for Identifier {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl std::ops::Deref for Identifier {
type Target = str;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
impl<'a> PartialEq<&'a str> for Identifier {
fn eq(&self, other: &&'a str) -> bool {
self.0.as_ref() == *other
}
}
impl<'a> PartialEq<Identifier> for &'a str {
fn eq(&self, other: &Identifier) -> bool {
other == self
}
}
impl std::fmt::Display for Identifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
std::fmt::Display::fmt(&self.0, f)
}
}
impl std::borrow::Borrow<str> for Identifier {
fn borrow(&self) -> &str {
self.0.as_ref()
}
}
impl Serialize for Identifier {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
debug_assert!(
is_valid_identifier(&self.0),
"all identifiers are validated on construction"
);
serializer.serialize_str(&self.0)
}
}
impl<'de> Deserialize<'de> for Identifier {
fn deserialize<D>(deserializer: D) -> Result<Identifier, D::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;
Identifier::new(string.as_str()).map_err(de::Error::custom)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn identifier_parsing() {
let valid_chars = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
assert!(Identifier::new(valid_chars).is_ok());
let i2 = Identifier::new("0aAƤ");
assert!(i2.is_err());
let i3 = Identifier::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
assert!(i3.is_err());
}
}