use core::fmt;
use std::{
borrow::Cow,
ops::{Add, AddAssign, Deref},
};
mod error;
pub use error::Error;
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct Identifier<'a> {
inner: Cow<'a, str>,
}
impl<'a> TryFrom<Cow<'a, str>> for Identifier<'a> {
type Error = Error;
fn try_from(value: Cow<'a, str>) -> Result<Self, Self::Error> {
if value.find('\0').is_some() {
return Err(Error::NullCharacter);
}
Ok(Identifier { inner: value })
}
}
impl<'a> TryFrom<&'a str> for Identifier<'a> {
type Error = Error;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
Identifier::try_from(Cow::Borrowed(value))
}
}
impl TryFrom<String> for Identifier<'static> {
type Error = Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
Identifier::try_from(Cow::Owned(value))
}
}
impl<'a> From<Identifier<'a>> for Cow<'a, str> {
fn from(value: Identifier<'a>) -> Self {
value.inner
}
}
impl<'a> Deref for Identifier<'a> {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<'a, T> AsRef<T> for Identifier<'a>
where
T: ?Sized,
<Identifier<'a> as Deref>::Target: AsRef<T>,
{
fn as_ref(&self) -> &T {
self.deref().as_ref()
}
}
impl<'a, 'b> Add<&Identifier<'b>> for Identifier<'a> {
type Output = Identifier<'a>;
fn add(mut self, rhs: &Identifier) -> Self::Output {
self += rhs;
self
}
}
impl<'a, 'b> AddAssign<&Identifier<'b>> for Identifier<'a> {
fn add_assign(&mut self, rhs: &Identifier) {
*self.inner.to_mut() += &rhs.inner;
}
}
impl<'a> fmt::Display for Identifier<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"")?;
let mut value: &str = &self.inner;
loop {
match value.find('"') {
Some(index) => {
write!(f, "{}", &value[..index])?;
write!(f, "\"\"")?;
value = &value[index + 1..];
}
None => {
write!(f, "{}", value)?;
break;
}
}
}
write!(f, "\"")
}
}
#[cfg(test)]
mod test {
use super::Identifier;
#[test]
fn tests() {
assert_eq!(
Identifier::try_from("main").unwrap().to_string(),
String::from("\"main\""),
);
assert_eq!(
Identifier::try_from(String::from("ma\"in"))
.unwrap()
.to_string(),
String::from("\"ma\"\"in\""),
);
assert_eq!(
Identifier::try_from(String::from("\"main"))
.unwrap()
.to_string(),
String::from("\"\"\"main\""),
);
assert_eq!(
Identifier::try_from(String::from("main\""))
.unwrap()
.to_string(),
String::from("\"main\"\"\""),
);
assert!(Identifier::try_from(String::from("ma\0in")).is_err());
}
#[test]
fn test_add() {
assert_eq!(
(Identifier::try_from(String::from("main")).unwrap()
+ &String::from("_after").try_into().unwrap())
.to_string(),
"\"main_after\"",
);
}
}