use std::fmt;
use crate::interner::{self, Symbol};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Id {
name: Symbol,
namespace: Option<Symbol>,
}
impl Id {
pub fn new(input: &str) -> Self {
let mut interner = interner::interner();
if let Some((ns_part, name_part)) = input.rsplit_once("::") {
let ns = interner.get_or_intern(ns_part);
let name = interner.get_or_intern(name_part);
Self {
name,
namespace: Some(ns),
}
} else {
let name = interner.get_or_intern(input);
Self {
name,
namespace: None,
}
}
}
pub fn from_anonymous() -> Self {
let mut interner = interner::interner();
let idx = interner.len();
let anon_name = format!("__{idx}");
let name_sym = interner.get_or_intern(&anon_name);
Self {
name: name_sym,
namespace: None,
}
}
pub fn create_nested(self, child_id: Id) -> Self {
if self.namespace.is_none() && child_id.namespace.is_none() {
return Self {
name: child_id.name,
namespace: Some(self.name),
};
}
let mut interner = interner::interner();
let parent_full = self.full_path_with(&interner);
let new_namespace_str = match child_id.namespace {
Some(child_ns) => {
let child_ns_str = interner.resolve(child_ns);
format!("{parent_full}::{child_ns_str}")
}
None => parent_full,
};
let new_namespace = interner.get_or_intern(&new_namespace_str);
Self {
name: child_id.name,
namespace: Some(new_namespace),
}
}
pub fn name(&self) -> &str {
interner::resolve(self.name)
}
pub fn namespace(&self) -> Option<&str> {
self.namespace.map(interner::resolve)
}
fn full_path(&self) -> String {
self.full_path_with(&interner::interner())
}
fn full_path_with(self, interner: &interner::Interner) -> String {
let name_str = interner.resolve(self.name);
match self.namespace {
Some(ns) => {
let ns_str = interner.resolve(ns);
format!("{ns_str}::{name_str}")
}
None => name_str.to_string(),
}
}
}
impl fmt::Display for Id {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.full_path())
}
}
impl std::str::FromStr for Id {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::new(s))
}
}
impl From<&str> for Id {
fn from(name: &str) -> Self {
Self::new(name)
}
}
impl PartialEq<str> for Id {
fn eq(&self, other: &str) -> bool {
let name;
let ns;
{
let interner = interner::interner();
name = interner.resolve(self.name);
ns = self.namespace.map(|ns| interner.resolve(ns));
}
match ns {
Some(ns) => {
let expected_len = ns.len() + 2 + name.len();
other.len() == expected_len
&& other.starts_with(ns)
&& other[ns.len()..].starts_with("::")
&& other[ns.len() + 2..] == *name
}
None => other == name,
}
}
}
impl PartialEq<&str> for Id {
fn eq(&self, other: &&str) -> bool {
self == *other
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let id1 = Id::new("Rectangle");
let id2 = Id::new("Rectangle");
let id3 = Id::new("Oval");
assert_eq!(id1, id2);
assert_ne!(id1, id3);
assert_eq!(id1, "Rectangle");
let namespaced = Id::new("system::backend");
assert_eq!(namespaced.name(), "backend");
assert_eq!(namespaced.namespace(), Some("system"));
assert_eq!(namespaced, "system::backend");
let deep = Id::new("a::b::c::d");
assert_eq!(deep.name(), "d");
assert_eq!(deep.namespace(), Some("a::b::c"));
assert_eq!(deep, "a::b::c::d");
}
#[test]
fn test_from_anonymous() {
let id1 = Id::from_anonymous();
let id2 = Id::from_anonymous();
assert_ne!(id1, id2);
assert_eq!(id1.namespace(), None);
assert_eq!(id2.namespace(), None);
}
#[test]
fn test_create_nested() {
let parent = Id::new("system");
let child1 = Id::new("backend");
let child2 = Id::new("frontend");
let nested1 = parent.create_nested(child1);
let nested2 = parent.create_nested(child2);
assert_ne!(nested1, nested2);
assert_eq!(nested1, "system::backend");
assert_eq!(nested2, "system::frontend");
let level1 = parent.create_nested(Id::new("frontend"));
let level2 = level1.create_nested(Id::new("app"));
let level3 = level2.create_nested(Id::new("component"));
assert_eq!(level3, "system::frontend::app::component");
let namespaced_child = parent.create_nested(Id::new("sub::component"));
assert_eq!(namespaced_child, "system::sub::component");
assert_eq!(namespaced_child.name(), "component");
assert_eq!(namespaced_child.namespace(), Some("system::sub"));
}
#[test]
fn test_name_and_namespace() {
let simple = Id::new("Rectangle");
assert_eq!(simple.name(), "Rectangle");
assert_eq!(simple.namespace(), None);
let nested = Id::new("system").create_nested(Id::new("backend"));
assert_eq!(nested.name(), "backend");
assert_eq!(nested.namespace(), Some("system"));
let deep = Id::new("system")
.create_nested(Id::new("frontend"))
.create_nested(Id::new("app"))
.create_nested(Id::new("component"));
assert_eq!(deep.name(), "component");
assert_eq!(deep.namespace(), Some("system::frontend::app"));
}
#[test]
fn test_equivalence_nested_and_new() {
let via_nested = Id::new("system").create_nested(Id::new("backend"));
let via_new = Id::new("system::backend");
assert_eq!(via_nested, via_new);
assert_eq!(via_nested.name(), via_new.name());
assert_eq!(via_nested.namespace(), via_new.namespace());
let deep_nested = Id::new("a")
.create_nested(Id::new("b"))
.create_nested(Id::new("c"));
let deep_new = Id::new("a::b::c");
assert_eq!(deep_nested, deep_new);
assert_eq!(deep_nested.name(), deep_new.name());
assert_eq!(deep_nested.namespace(), deep_new.namespace());
}
#[test]
fn test_display() {
assert_eq!(format!("{}", Id::new("Rectangle")), "Rectangle");
let nested = Id::new("parent").create_nested(Id::new("child"));
assert_eq!(format!("{}", nested), "parent::child");
let deep = Id::new("system")
.create_nested(Id::new("frontend"))
.create_nested(Id::new("app"));
assert_eq!(format!("{}", deep), "system::frontend::app");
}
#[test]
fn test_from_str() {
let from_into: Id = "test_string".into();
assert_eq!(from_into, Id::new("test_string"));
let parsed: Id = "parent::child".parse().unwrap();
assert_eq!(parsed.name(), "child");
assert_eq!(parsed.namespace(), Some("parent"));
assert_eq!(parsed, "parent::child");
}
#[test]
fn test_partial_eq_str() {
let id = Id::new("Rectangle");
assert!(id == "Rectangle");
assert!(id != "Oval");
let nested = Id::new("parent::child");
assert!(nested == "parent::child");
assert!(nested != "parent");
assert!(nested != "child");
let empty = Id::new("");
assert!(empty == "");
assert!(empty != "non-empty");
let name = String::from("Rectangle");
assert!(id == name.as_str());
}
#[test]
fn test_copy() {
let id1 = Id::new("copy_test");
let id2 = id1;
let id3 = id1;
assert_eq!(id1, id2);
assert_eq!(id1, id3);
assert_eq!(id1, "copy_test");
}
}