use anyhow::{bail, Result};
use ref_cast::RefCast;
use serde::{Deserialize, Serialize};
use std::{borrow::Borrow, fmt, ops::Deref};
pub fn is_valid_identifier_char(c: char) -> bool {
matches!(c, '_' | 'a'..='z' | 'A'..='Z' | '0'..='9')
}
fn is_valid(s: &str) -> bool {
if s == "<SELF>" {
return true;
}
let len = s.len();
let mut chars = s.chars();
match chars.next() {
Some('a'..='z') | Some('A'..='Z') => chars.all(is_valid_identifier_char),
Some('_') if len > 1 => chars.all(is_valid_identifier_char),
_ => false,
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Identifier(Box<str>);
impl Identifier {
pub fn new(s: impl Into<Box<str>>) -> Result<Self> {
let s = s.into();
if Self::is_valid(&s) {
Ok(Self(s))
} else {
bail!("Invalid identifier '{}'", s);
}
}
pub fn is_valid(s: impl AsRef<str>) -> bool {
is_valid(s.as_ref())
}
pub fn is_self(&self) -> bool {
&*self.0 == "<SELF>"
}
pub fn from_utf8(vec: Vec<u8>) -> Result<Self> {
let s = String::from_utf8(vec)?;
Self::new(s)
}
pub fn as_ident_str(&self) -> &IdentStr {
self
}
pub fn into_string(self) -> String {
self.0.into()
}
pub fn into_bytes(self) -> Vec<u8> {
self.into_string().into_bytes()
}
}
impl<'a> From<&'a IdentStr> for Identifier {
fn from(ident_str: &'a IdentStr) -> Self {
ident_str.to_owned()
}
}
impl AsRef<IdentStr> for Identifier {
fn as_ref(&self) -> &IdentStr {
self
}
}
impl Deref for Identifier {
type Target = IdentStr;
fn deref(&self) -> &IdentStr {
IdentStr::ref_cast(&self.0)
}
}
impl fmt::Display for Identifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &self.0)
}
}
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd, RefCast)]
#[repr(transparent)]
pub struct IdentStr(str);
impl IdentStr {
pub fn new(s: &str) -> Result<&IdentStr> {
if Self::is_valid(s) {
Ok(IdentStr::ref_cast(s))
} else {
bail!("Invalid identifier '{}'", s);
}
}
pub fn is_valid(s: impl AsRef<str>) -> bool {
is_valid(s.as_ref())
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
}
impl Borrow<IdentStr> for Identifier {
fn borrow(&self) -> &IdentStr {
self
}
}
impl ToOwned for IdentStr {
type Owned = Identifier;
fn to_owned(&self) -> Identifier {
Identifier(self.0.into())
}
}
impl fmt::Display for IdentStr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &self.0)
}
}