use rustc_hash::FxHashSet;
use crate::{
node::{ElementNode, FromAnyValue, NodeType, OwnedAttributeView},
prelude::AttributeName,
tags::TagName,
NodeId,
};
#[derive(Debug)]
pub struct NodeView<'a, V: FromAnyValue = ()> {
id: NodeId,
inner: &'a NodeType<V>,
mask: &'a NodeMask,
height: u16,
}
impl<'a, V: FromAnyValue> NodeView<'a, V> {
pub fn new(id: NodeId, node: &'a NodeType<V>, view: &'a NodeMask, height: u16) -> Self {
Self {
inner: node,
mask: view,
id,
height,
}
}
pub fn node_type(&self) -> &'a NodeType<V> {
self.inner
}
pub fn node_id(&self) -> NodeId {
self.id
}
pub fn height(&self) -> u16 {
self.height
}
pub fn tag(&self) -> Option<&'a TagName> {
self.mask
.tag
.then_some(match &self.inner {
NodeType::Element(ElementNode { tag, .. }) => Some(tag),
_ => 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))
.map(|(attr, val)| OwnedAttributeView {
attribute: attr,
value: val,
}),
),
_ => None,
}
}
pub fn text(&self) -> Option<&str> {
self.mask.text.then_some(self.inner.text()).flatten()
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum AttributeMask {
All,
Some(FxHashSet<AttributeName>),
}
impl AttributeMask {
pub fn contains(&self, attr: &AttributeName) -> bool {
match self {
AttributeMask::All => true,
AttributeMask::Some(attrs) => attrs.contains(attr),
}
}
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,
text: bool,
listeners: bool,
}
impl NodeMask {
pub fn overlaps(&self, other: &Self) -> bool {
(self.tag && other.tag)
|| 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,
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_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 [AttributeName]),
}
impl Default for AttributeMaskBuilder<'_> {
fn default() -> Self {
AttributeMaskBuilder::Some(&[])
}
}
#[derive(Default, PartialEq, Eq, Clone, Debug)]
pub struct NodeMaskBuilder<'a> {
attritutes: AttributeMaskBuilder<'a>,
tag: 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_tag()
.with_listeners();
pub const fn new() -> Self {
Self {
attritutes: AttributeMaskBuilder::Some(&[]),
tag: 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_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(FxHashSet::from_iter(attrs.iter().copied()))
}
},
tag: self.tag,
text: self.text,
listeners: self.listeners,
}
}
}