freya_native_core/
node_ref.rs

1//! Utilities that provide limited access to nodes
2
3use rustc_hash::FxHashSet;
4
5use crate::{
6    node::{
7        ElementNode,
8        FromAnyValue,
9        NodeType,
10        OwnedAttributeView,
11    },
12    prelude::AttributeName,
13    tags::TagName,
14    NodeId,
15};
16
17/// A view into a [NodeType] with a mask that determines what is visible.
18#[derive(Debug)]
19pub struct NodeView<'a, V: FromAnyValue = ()> {
20    id: NodeId,
21    inner: &'a NodeType<V>,
22    mask: &'a NodeMask,
23    height: u16,
24}
25
26impl<'a, V: FromAnyValue> NodeView<'a, V> {
27    /// Create a new NodeView from a VNode, and mask.
28    pub fn new(id: NodeId, node: &'a NodeType<V>, view: &'a NodeMask, height: u16) -> Self {
29        Self {
30            inner: node,
31            mask: view,
32            id,
33            height,
34        }
35    }
36
37    pub fn node_type(&self) -> &'a NodeType<V> {
38        self.inner
39    }
40
41    /// Get the node id of the node
42    pub fn node_id(&self) -> NodeId {
43        self.id
44    }
45
46    /// Get the node height
47    pub fn height(&self) -> u16 {
48        self.height
49    }
50
51    /// Get the tag of the node if the tag is enabled in the mask
52    pub fn tag(&self) -> Option<&'a TagName> {
53        self.mask
54            .tag
55            .then_some(match &self.inner {
56                NodeType::Element(ElementNode { tag, .. }) => Some(tag),
57                _ => None,
58            })
59            .flatten()
60    }
61
62    /// Get any attributes that are enabled in the mask
63    pub fn attributes<'b>(
64        &'b self,
65    ) -> Option<impl Iterator<Item = OwnedAttributeView<'a, V>> + 'b> {
66        match &self.inner {
67            NodeType::Element(ElementNode { attributes, .. }) => Some(
68                attributes
69                    .iter()
70                    .filter(move |(attr, _)| self.mask.attritutes.contains(attr))
71                    .map(|(attr, val)| OwnedAttributeView {
72                        attribute: attr,
73                        value: val,
74                    }),
75            ),
76            _ => None,
77        }
78    }
79
80    /// Get the text if it is enabled in the mask
81    pub fn text(&self) -> Option<&str> {
82        self.mask.text.then_some(self.inner.text()).flatten()
83    }
84}
85
86/// A mask that contains a list of attributes that are visible.
87#[derive(PartialEq, Eq, Clone, Debug)]
88pub enum AttributeMask {
89    /// All attributes are visible
90    All,
91    /// Only the given attributes are visible
92    Some(FxHashSet<AttributeName>),
93}
94
95impl AttributeMask {
96    /// Check if the mask contains the given attribute
97    pub fn contains(&self, attr: &AttributeName) -> bool {
98        match self {
99            AttributeMask::All => true,
100            AttributeMask::Some(attrs) => attrs.contains(attr),
101        }
102    }
103
104    /// Combine two attribute masks
105    pub fn union(&self, other: &Self) -> Self {
106        match (self, other) {
107            (AttributeMask::Some(s), AttributeMask::Some(o)) => {
108                AttributeMask::Some(s.union(o).cloned().collect())
109            }
110            _ => AttributeMask::All,
111        }
112    }
113
114    /// Check if two attribute masks overlap
115    fn overlaps(&self, other: &Self) -> bool {
116        match (self, other) {
117            (AttributeMask::All, AttributeMask::Some(attrs)) => !attrs.is_empty(),
118            (AttributeMask::Some(attrs), AttributeMask::All) => !attrs.is_empty(),
119            (AttributeMask::Some(attrs1), AttributeMask::Some(attrs2)) => {
120                !attrs1.is_disjoint(attrs2)
121            }
122            _ => true,
123        }
124    }
125}
126
127impl Default for AttributeMask {
128    fn default() -> Self {
129        AttributeMask::Some(FxHashSet::default())
130    }
131}
132
133/// A mask that limits what parts of a node a dependency can see.
134#[derive(Default, PartialEq, Eq, Clone, Debug)]
135pub struct NodeMask {
136    attritutes: AttributeMask,
137    tag: bool,
138    text: bool,
139    listeners: bool,
140}
141
142impl NodeMask {
143    /// Check if two masks overlap
144    pub fn overlaps(&self, other: &Self) -> bool {
145        (self.tag && other.tag)
146            || self.attritutes.overlaps(&other.attritutes)
147            || (self.text && other.text)
148            || (self.listeners && other.listeners)
149    }
150
151    /// Combine two node masks
152    pub fn union(&self, other: &Self) -> Self {
153        Self {
154            attritutes: self.attritutes.union(&other.attritutes),
155            tag: self.tag | other.tag,
156            text: self.text | other.text,
157            listeners: self.listeners | other.listeners,
158        }
159    }
160
161    /// Allow the mask to view the given attributes
162    pub fn add_attributes(&mut self, attributes: AttributeMask) {
163        self.attritutes = self.attritutes.union(&attributes);
164    }
165
166    /// Get the mask for the attributes
167    pub fn attributes(&self) -> &AttributeMask {
168        &self.attritutes
169    }
170
171    /// Set the mask to view the tag
172    pub fn set_tag(&mut self) {
173        self.tag = true;
174    }
175
176    /// Get the mask for the tag
177    pub fn tag(&self) -> bool {
178        self.tag
179    }
180
181    /// Set the mask to view the text
182    pub fn set_text(&mut self) {
183        self.text = true;
184    }
185
186    /// Get the mask for the text
187    pub fn text(&self) -> bool {
188        self.text
189    }
190
191    /// Set the mask to view the listeners
192    pub fn set_listeners(&mut self) {
193        self.listeners = true;
194    }
195
196    /// Get the mask for the listeners
197    pub fn listeners(&self) -> bool {
198        self.listeners
199    }
200}
201
202/// A builder for a mask that controls what attributes are visible.
203#[derive(PartialEq, Eq, Clone, Debug)]
204pub enum AttributeMaskBuilder<'a> {
205    /// All attributes are visible
206    All,
207    /// Only the given attributes are visible
208    Some(&'a [AttributeName]),
209}
210
211impl Default for AttributeMaskBuilder<'_> {
212    fn default() -> Self {
213        AttributeMaskBuilder::Some(&[])
214    }
215}
216
217/// A mask that limits what parts of a node a dependency can see.
218#[derive(Default, PartialEq, Eq, Clone, Debug)]
219pub struct NodeMaskBuilder<'a> {
220    attritutes: AttributeMaskBuilder<'a>,
221    tag: bool,
222    text: bool,
223    listeners: bool,
224}
225
226impl<'a> NodeMaskBuilder<'a> {
227    /// A node mask with no parts visible.
228    pub const NONE: Self = Self::new();
229    /// A node mask with every part visible.
230    pub const ALL: Self = Self::new()
231        .with_attrs(AttributeMaskBuilder::All)
232        .with_text()
233        .with_tag()
234        .with_listeners();
235
236    /// Create a empty node mask
237    pub const fn new() -> Self {
238        Self {
239            attritutes: AttributeMaskBuilder::Some(&[]),
240            tag: false,
241            text: false,
242            listeners: false,
243        }
244    }
245
246    /// Allow the mask to view the given attributes
247    pub const fn with_attrs(mut self, attritutes: AttributeMaskBuilder<'a>) -> Self {
248        self.attritutes = attritutes;
249        self
250    }
251
252    /// Allow the mask to view the tag
253    pub const fn with_tag(mut self) -> Self {
254        self.tag = true;
255        self
256    }
257
258    /// Allow the mask to view the text
259    pub const fn with_text(mut self) -> Self {
260        self.text = true;
261        self
262    }
263
264    /// Allow the mask to view the listeners
265    pub const fn with_listeners(mut self) -> Self {
266        self.listeners = true;
267        self
268    }
269
270    /// Build the mask
271    pub fn build(self) -> NodeMask {
272        NodeMask {
273            attritutes: match self.attritutes {
274                AttributeMaskBuilder::All => AttributeMask::All,
275                AttributeMaskBuilder::Some(attrs) => {
276                    AttributeMask::Some(FxHashSet::from_iter(attrs.iter().copied()))
277                }
278            },
279            tag: self.tag,
280            text: self.text,
281            listeners: self.listeners,
282        }
283    }
284}