use thiserror::Error;
#[derive(Debug, PartialEq, Eq, derive_more::Display)]
pub struct NodeName(String);
#[derive(Clone, Debug, Error)]
#[error("invalid node name {0}")]
pub struct NodeNameError(String);
impl NodeName {
pub fn new(name: impl Into<String>) -> Result<Self, NodeNameError> {
let name = name.into();
if Self::validate(&name) {
Ok(Self(name))
} else {
Err(NodeNameError(name))
}
}
#[must_use]
pub unsafe fn new_unchecked(name: impl Into<String>) -> Self {
let name: String = name.into();
debug_assert!(Self::validate(&name));
Self(name)
}
#[must_use]
pub const fn root() -> Self {
Self(String::new())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn validate(node_name: &str) -> bool {
node_name.is_empty()
|| (!node_name.contains('/')
&& !node_name.starts_with("__")
&& !node_name.replace('.', "").is_empty())
}
#[must_use]
pub fn is_root(&self) -> bool {
self.0.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn node_name() {
assert_eq!(NodeName::root(), NodeName::new("").unwrap());
assert!(NodeName::new("").unwrap().is_root());
assert!(NodeName::new("a").is_ok());
assert_eq!(NodeName::new("a").unwrap().to_string(), "a");
assert!(NodeName::new("a/b").is_err());
assert_eq!(
NodeName::new("a/b").unwrap_err().to_string(),
"invalid node name a/b"
);
assert!(NodeName::new("__").is_err());
assert!(NodeName::new(".").is_err());
assert!(NodeName::new("..").is_err());
}
}