use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex, RwLock, Weak};
use markup5ever::QualName;
pub type AttrAtom = string_cache::Atom<string_cache::EmptyStaticAtomSet>;
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
pub struct Attribute {
pub name: QualName,
pub value: AttrAtom,
}
#[derive(Debug)]
pub struct AttributeNode {
pub name: QualName,
pub value: AttrAtom,
pub attached: bool,
}
#[derive(Debug)]
pub struct Attributes {
inner: Vec<Attribute>,
handles: Mutex<Vec<Weak<RwLock<AttributeNode>>>>,
}
impl Clone for Attributes {
fn clone(&self) -> Self {
Self::new(self.inner.clone())
}
}
impl Drop for Attributes {
fn drop(&mut self) {
for handle in self
.handles
.get_mut()
.unwrap()
.iter()
.filter_map(Weak::upgrade)
{
handle.write().unwrap().attached = false;
}
}
}
fn same_expanded_name(left: &QualName, right: &QualName) -> bool {
left.ns == right.ns && left.local == right.local
}
impl Attributes {
pub fn new(inner: Vec<Attribute>) -> Self {
Self {
inner,
handles: Mutex::new(Vec::new()),
}
}
pub fn get(&mut self, name: &QualName) -> Option<&Attribute> {
self.inner
.iter()
.find(|attr| same_expanded_name(&attr.name, name))
}
pub fn attribute_node(&self, name: &QualName) -> Option<Arc<RwLock<AttributeNode>>> {
let attr = self
.inner
.iter()
.find(|attr| same_expanded_name(&attr.name, name))?;
let mut handles = self.handles.lock().unwrap();
handles.retain(|handle| handle.strong_count() != 0);
for handle in handles.iter().filter_map(Weak::upgrade) {
let matches = {
let node = handle.read().unwrap();
node.attached && same_expanded_name(&node.name, &attr.name)
};
if matches {
return Some(handle);
}
}
let handle = Arc::new(RwLock::new(AttributeNode {
name: attr.name.clone(),
value: attr.value.clone(),
attached: true,
}));
handles.push(Arc::downgrade(&handle));
Some(handle)
}
pub fn detach_attribute_node(&self, name: &QualName) {
self.handles.lock().unwrap().retain(|weak| {
let Some(handle) = weak.upgrade() else {
return false;
};
let mut node = handle.write().unwrap();
if same_expanded_name(&node.name, name) {
node.attached = false;
false
} else {
true
}
});
}
pub fn bind_attribute_node(&self, handle: &Arc<RwLock<AttributeNode>>) {
let name = handle.read().unwrap().name.clone();
self.detach_attribute_node(&name);
handle.write().unwrap().attached = true;
self.handles.lock().unwrap().push(Arc::downgrade(handle));
}
pub fn set(&mut self, name: QualName, value: &str) {
let existing_attr = self
.inner
.iter_mut()
.find(|attr| same_expanded_name(&attr.name, &name));
let value = AttrAtom::from(value);
if let Some(existing_attr) = existing_attr {
existing_attr.name = name.clone();
existing_attr.value = value.clone();
} else {
self.inner.push(Attribute {
name: name.clone(),
value: value.clone(),
});
}
self.handles.get_mut().unwrap().retain(|weak| {
let Some(handle) = weak.upgrade() else {
return false;
};
let mut node = handle.write().unwrap();
if node.attached && same_expanded_name(&node.name, &name) {
node.name = name.clone();
node.value = value.clone();
}
true
});
}
pub fn remove(&mut self, name: &QualName) -> Option<Attribute> {
let idx = self
.inner
.iter()
.position(|attr| same_expanded_name(&attr.name, name))?;
self.detach_attribute_node(name);
Some(self.inner.remove(idx))
}
}
impl Deref for Attributes {
type Target = Vec<Attribute>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for Attributes {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}