use std::str::FromStr;
use std::sync::Arc;
use serde::{Deserialize, Deserializer};
use crate::error::NamingError;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
#[serde(transparent)]
#[cfg_attr(feature = "druid", derive(druid::Data))]
pub struct Name(Arc<str>);
impl Name {
pub fn new(name: &str) -> Result<Name, NamingError> {
if is_valid(name) {
Ok(Name(name.into()))
} else {
Err(NamingError::Invalid(name.into()))
}
}
pub(crate) fn new_raw(name: &str) -> Name {
assert!(is_valid(name));
Name(name.into())
}
pub fn as_str(&self) -> &str {
self.as_ref()
}
}
fn is_valid(name: &str) -> bool {
!(name.is_empty()
|| name.chars().any(|b| {
(0x0..=0x1f).contains(&(b as u32))
|| (0x80..=0x9f).contains(&(b as u32))
|| b as u32 == 0x7f
}))
}
impl AsRef<str> for Name {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl std::ops::Deref for Name {
type Target = str;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
impl<'a> PartialEq<&'a str> for Name {
fn eq(&self, other: &&'a str) -> bool {
self.0.as_ref() == *other
}
}
impl<'a> PartialEq<Name> for &'a str {
fn eq(&self, other: &Name) -> bool {
other == self
}
}
impl std::fmt::Display for Name {
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 Name {
fn borrow(&self) -> &str {
self.0.as_ref()
}
}
impl FromStr for Name {
type Err = NamingError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Name::new(s)
}
}
impl<'de> Deserialize<'de> for Name {
fn deserialize<D>(deserializer: D) -> Result<Name, D::Error>
where
D: Deserializer<'de>,
{
let s: Arc<str> = Deserialize::deserialize(deserializer)?;
if is_valid(&s) {
Ok(Name(s))
} else {
Err(serde::de::Error::custom(NamingError::Invalid(s.to_string())))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn assert_eq_str() {
assert_eq!(Name::new_raw("hi"), "hi");
assert_eq!("hi", Name::new_raw("hi"));
assert_eq!("hi 💖", Name::new_raw("hi 💖"));
assert_eq!(vec![Name::new_raw("a"), Name::new_raw("b")], vec!["a", "b"]);
assert_eq!(vec!["a", "b"], vec![Name::new_raw("a"), Name::new_raw("b")]);
}
}