use rustc_hash::FxHashSet;
use crate::{
node::{ElementNode, FromAnyValue, NodeType, OwnedAttributeView},
NodeId,
};
#[derive(Debug)]
pub struct NodeView<'a, V: FromAnyValue = ()> {
id: NodeId,
inner: &'a NodeType<V>,
mask: &'a NodeMask,
}
impl<'a, V: FromAnyValue> NodeView<'a, V> {
pub fn new(id: NodeId, node: &'a NodeType<V>, view: &'a NodeMask) -> Self {
Self {
inner: node,
mask: view,
id,
}
}
pub fn node_id(&self) -> NodeId {
self.id
}
pub fn tag(&self) -> Option<&'a str> {
self.mask
.tag
.then_some(match &self.inner {
NodeType::Element(ElementNode { tag, .. }) => Some(&**tag),
_ => None,
})
.flatten()
}
pub fn namespace(&self) -> Option<&'a str> {
self.mask
.namespace
.then_some(match &self.inner {
NodeType::Element(ElementNode { namespace, .. }) => namespace.as_deref(),
_ => None,
})
.flatten()
}
pub fn attributes<'b>(
&'b self,
) -> Option<impl Iterator<Item = OwnedAttributeView<'a, V>> + 'b> {
match &self.inner {
NodeType::Element(ElementNode { attributes, .. }) => Some(
attributes
.iter()
.filter(move |(attr, _)| self.mask.attritutes.contains(&attr.name))
.map(|(attr, val)| OwnedAttributeView {
attribute: attr,
value: val,
}),
),
_ => None,
}
}
pub fn text(&self) -> Option<&str> {
self.mask
.text
.then_some(match &self.inner {
NodeType::Text(text) => Some(&text.text),
_ => None,
})
.flatten()
.map(|x| &**x)
}
pub fn listeners(&self) -> Option<impl Iterator<Item = &'a str> + '_> {
if self.mask.listeners {
match &self.inner {
NodeType::Element(ElementNode { listeners, .. }) => {
Some(listeners.iter().map(|l| &**l))
}
_ => None,
}
} else {
None
}
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum AttributeMask {
All,
Some(FxHashSet<Box<str>>),
}
impl AttributeMask {
pub fn contains(&self, attr: &str) -> bool {
match self {
AttributeMask::All => true,
AttributeMask::Some(attrs) => attrs.contains(attr),
}
}
pub fn single(new: &str) -> Self {
let mut set = FxHashSet::default();
set.insert(new.into());
Self::Some(set)
}
pub fn union(&self, other: &Self) -> Self {
match (self, other) {
(AttributeMask::Some(s), AttributeMask::Some(o)) => {
AttributeMask::Some(s.union(o).cloned().collect())
}
_ => AttributeMask::All,
}
}
fn overlaps(&self, other: &Self) -> bool {
match (self, other) {
(AttributeMask::All, AttributeMask::Some(attrs)) => !attrs.is_empty(),
(AttributeMask::Some(attrs), AttributeMask::All) => !attrs.is_empty(),
(AttributeMask::Some(attrs1), AttributeMask::Some(attrs2)) => {
!attrs1.is_disjoint(attrs2)
}
_ => true,
}
}
}
impl Default for AttributeMask {
fn default() -> Self {
AttributeMask::Some(FxHashSet::default())
}
}
#[derive(Default, PartialEq, Eq, Clone, Debug)]
pub struct NodeMask {
attritutes: AttributeMask,
tag: bool,
namespace: bool,
text: bool,
listeners: bool,
}
impl NodeMask {
pub fn overlaps(&self, other: &Self) -> bool {
(self.tag && other.tag)
|| (self.namespace && other.namespace)
|| self.attritutes.overlaps(&other.attritutes)
|| (self.text && other.text)
|| (self.listeners && other.listeners)
}
pub fn union(&self, other: &Self) -> Self {
Self {
attritutes: self.attritutes.union(&other.attritutes),
tag: self.tag | other.tag,
namespace: self.namespace | other.namespace,
text: self.text | other.text,
listeners: self.listeners | other.listeners,
}
}
pub fn add_attributes(&mut self, attributes: AttributeMask) {
self.attritutes = self.attritutes.union(&attributes);
}
pub fn attributes(&self) -> &AttributeMask {
&self.attritutes
}
pub fn set_tag(&mut self) {
self.tag = true;
}
pub fn tag(&self) -> bool {
self.tag
}
pub fn set_namespace(&mut self) {
self.namespace = true;
}
pub fn namespace(&self) -> bool {
self.namespace
}
pub fn set_text(&mut self) {
self.text = true;
}
pub fn text(&self) -> bool {
self.text
}
pub fn set_listeners(&mut self) {
self.listeners = true;
}
pub fn listeners(&self) -> bool {
self.listeners
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum AttributeMaskBuilder<'a> {
All,
Some(&'a [&'a str]),
}
impl Default for AttributeMaskBuilder<'_> {
fn default() -> Self {
AttributeMaskBuilder::Some(&[])
}
}
#[derive(Default, PartialEq, Eq, Clone, Debug)]
pub struct NodeMaskBuilder<'a> {
attritutes: AttributeMaskBuilder<'a>,
tag: bool,
namespace: bool,
text: bool,
listeners: bool,
}
impl<'a> NodeMaskBuilder<'a> {
pub const NONE: Self = Self::new();
pub const ALL: Self = Self::new()
.with_attrs(AttributeMaskBuilder::All)
.with_text()
.with_element()
.with_listeners();
pub const fn new() -> Self {
Self {
attritutes: AttributeMaskBuilder::Some(&[]),
tag: false,
namespace: false,
text: false,
listeners: false,
}
}
pub const fn with_attrs(mut self, attritutes: AttributeMaskBuilder<'a>) -> Self {
self.attritutes = attritutes;
self
}
pub const fn with_tag(mut self) -> Self {
self.tag = true;
self
}
pub const fn with_namespace(mut self) -> Self {
self.namespace = true;
self
}
pub const fn with_element(self) -> Self {
self.with_namespace().with_tag()
}
pub const fn with_text(mut self) -> Self {
self.text = true;
self
}
pub const fn with_listeners(mut self) -> Self {
self.listeners = true;
self
}
pub fn build(self) -> NodeMask {
NodeMask {
attritutes: match self.attritutes {
AttributeMaskBuilder::All => AttributeMask::All,
AttributeMaskBuilder::Some(attrs) => {
AttributeMask::Some(attrs.iter().map(|s| (*s).into()).collect())
}
},
tag: self.tag,
namespace: self.namespace,
text: self.text,
listeners: self.listeners,
}
}
}