use std::{
cell::{Ref, RefCell},
rc::Rc,
};
use indexmap::IndexMap;
use uuid::Uuid as UUID;
use crate::attribute::Attribute;
#[derive(Clone, Debug)]
pub struct Element(Rc<RefCell<ElementData>>);
impl Default for Element {
fn default() -> Self {
Self(Rc::new(RefCell::new(ElementData {
name: String::from(Self::DEFAULT_ELEMENT_NAME),
class: String::from(Self::DEFAULT_ELEMENT_CLASS),
id: UUID::new_v4(),
attributes: IndexMap::new(),
})))
}
}
impl PartialEq for Element {
fn eq(&self, other: &Self) -> bool {
self.0.borrow().id == other.0.borrow().id
}
}
impl Eq for Element {}
impl std::hash::Hash for Element {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.borrow().id.hash(state);
}
}
impl Element {
pub const DEFAULT_ELEMENT_NAME: &str = "unnamed";
pub const DEFAULT_ELEMENT_CLASS: &str = "DmElement";
pub fn create(name: impl Into<String>, class: impl Into<String>) -> Self {
Self(Rc::new(RefCell::new(ElementData {
name: name.into(),
class: class.into(),
id: UUID::new_v4(),
attributes: IndexMap::new(),
})))
}
pub fn named(name: impl Into<String>) -> Self {
Self(Rc::new(RefCell::new(ElementData {
name: name.into(),
class: String::from(Self::DEFAULT_ELEMENT_CLASS),
id: UUID::new_v4(),
attributes: IndexMap::new(),
})))
}
pub fn class(class: impl Into<String>) -> Self {
Self(Rc::new(RefCell::new(ElementData {
name: String::from(Self::DEFAULT_ELEMENT_NAME),
class: class.into(),
id: UUID::new_v4(),
attributes: IndexMap::new(),
})))
}
pub fn full(name: impl Into<String>, class: impl Into<String>, id: UUID) -> Self {
Self(Rc::new(RefCell::new(ElementData {
name: name.into(),
class: class.into(),
id,
attributes: IndexMap::new(),
})))
}
pub fn get_name(&'_ self) -> Ref<'_, String> {
let element_data = self.0.borrow();
Ref::map(element_data, |element| &element.name)
}
pub fn set_name(&self, name: impl Into<String>) {
let mut element_data = self.0.borrow_mut();
element_data.name = name.into();
}
pub fn get_class(&'_ self) -> Ref<'_, String> {
let element_data = self.0.borrow();
Ref::map(element_data, |element| &element.class)
}
pub fn set_class(&self, class: impl Into<String>) {
let mut element_data = self.0.borrow_mut();
element_data.class = class.into();
}
pub fn get_id(&'_ self) -> Ref<'_, UUID> {
let element_data = self.0.borrow();
Ref::map(element_data, |element: &ElementData| &element.id)
}
pub fn set_id(&self, id: UUID) {
let mut element_data = self.0.borrow_mut();
element_data.id = id;
}
pub fn get_attribute(&'_ self, name: impl AsRef<str>) -> Option<Ref<'_, Attribute>> {
let element_data = self.0.borrow();
let attribute_name = name.as_ref();
Ref::filter_map(element_data, |element| element.attributes.get(attribute_name)).ok()
}
pub fn set_attribute(&mut self, name: impl Into<String>, attribute: Attribute) -> Option<Attribute> {
let mut element_data = self.0.borrow_mut();
let attribute_name = name.into();
if attribute_name.eq("name") || attribute_name.eq("id") {
return None;
}
element_data.attributes.insert(attribute_name, attribute)
}
pub fn remove_attribute(&mut self, name: impl AsRef<str>) -> Option<Attribute> {
let mut element_data = self.0.borrow_mut();
let attribute_name = name.as_ref();
element_data.attributes.shift_remove(attribute_name)
}
pub fn get_value<V>(&'_ self, name: impl AsRef<str>) -> Option<Ref<'_, V>>
where
for<'a> &'a V: TryFrom<&'a Attribute>,
{
let element_data = self.0.borrow();
let attribute_name = name.as_ref();
let element_attribute = Ref::filter_map(element_data, |element| element.attributes.get(attribute_name)).ok()?;
Ref::filter_map(element_attribute, |attribute| attribute.try_into().ok()).ok()
}
pub fn set_value<V>(&mut self, name: impl Into<String>, value: V) -> Option<V>
where
V: Into<Attribute> + TryFrom<Attribute>,
{
let mut element_data = self.0.borrow_mut();
let attribute_name = name.into();
let attribute_value = value.into();
if attribute_name.eq("name") || attribute_name.eq("id") {
return None;
}
element_data
.attributes
.insert(attribute_name, attribute_value)
.and_then(|attribute| attribute.try_into().ok())
}
pub fn remove_value<V: TryFrom<Attribute>>(&mut self, name: impl AsRef<str>) -> Option<V> {
let attribute = self.remove_attribute(name)?;
V::try_from(attribute).ok()
}
pub fn get_attributes(&'_ self) -> Ref<'_, IndexMap<String, Attribute>> {
let element_data = self.0.borrow();
Ref::map(element_data, |element| &element.attributes)
}
pub fn reserve_attributes(&mut self, additional: usize) {
let mut element_data = self.0.borrow_mut();
element_data.attributes.reserve(additional);
}
}
#[derive(Debug)]
struct ElementData {
name: String,
class: String,
id: UUID,
attributes: IndexMap<String, Attribute>,
}