kuchikikiki/
node_data_ref.rs

1use crate::tree::{Doctype, DocumentData, ElementData, Node, NodeRef};
2use std::cell::RefCell;
3use std::fmt;
4use std::ops::Deref;
5
6impl NodeRef {
7    /// If this node is an element, return a strong reference to element-specific data.
8    #[inline]
9    pub fn into_element_ref(self) -> Option<NodeDataRef<ElementData>> {
10        NodeDataRef::new_opt(self, Node::as_element)
11    }
12
13    /// If this node is a text node, return a strong reference to its contents.
14    #[inline]
15    pub fn into_text_ref(self) -> Option<NodeDataRef<RefCell<String>>> {
16        NodeDataRef::new_opt(self, Node::as_text)
17    }
18
19    /// If this node is a comment, return a strong reference to its contents.
20    #[inline]
21    pub fn into_comment_ref(self) -> Option<NodeDataRef<RefCell<String>>> {
22        NodeDataRef::new_opt(self, Node::as_comment)
23    }
24
25    /// If this node is a doctype, return a strong reference to doctype-specific data.
26    #[inline]
27    pub fn into_doctype_ref(self) -> Option<NodeDataRef<Doctype>> {
28        NodeDataRef::new_opt(self, Node::as_doctype)
29    }
30
31    /// If this node is a document, return a strong reference to document-specific data.
32    #[inline]
33    pub fn into_document_ref(self) -> Option<NodeDataRef<DocumentData>> {
34        NodeDataRef::new_opt(self, Node::as_document)
35    }
36}
37
38/// Holds a strong reference to a node, but dereferences to some component inside of it.
39#[derive(Eq)]
40pub struct NodeDataRef<T> {
41    _keep_alive: NodeRef,
42    _reference: *const T,
43}
44
45impl<T> NodeDataRef<T> {
46    /// Create a `NodeDataRef` for a component in a given node.
47    #[inline]
48    pub fn new<F>(rc: NodeRef, f: F) -> NodeDataRef<T>
49    where
50        F: FnOnce(&Node) -> &T,
51    {
52        NodeDataRef {
53            _reference: f(&rc),
54            _keep_alive: rc,
55        }
56    }
57
58    /// Create a `NodeDataRef` for and a component that may or may not be in a given node.
59    #[inline]
60    pub fn new_opt<F>(rc: NodeRef, f: F) -> Option<NodeDataRef<T>>
61    where
62        F: FnOnce(&Node) -> Option<&T>,
63    {
64        f(&rc).map(|r| r as *const T).map(move |r| NodeDataRef {
65            _reference: r,
66            _keep_alive: rc,
67        })
68    }
69
70    /// Access the corresponding node.
71    #[inline]
72    pub fn as_node(&self) -> &NodeRef {
73        &self._keep_alive
74    }
75}
76
77impl<T> Deref for NodeDataRef<T> {
78    type Target = T;
79    #[inline]
80    fn deref(&self) -> &T {
81        unsafe { &*self._reference }
82    }
83}
84
85// #[derive(PartialEq)] would compare both fields
86impl<T> PartialEq for NodeDataRef<T> {
87    #[inline]
88    fn eq(&self, other: &Self) -> bool {
89        self._keep_alive == other._keep_alive
90    }
91}
92
93// #[derive(Clone)] would have an unnecessary `T: Clone` bound
94impl<T> Clone for NodeDataRef<T> {
95    #[inline]
96    fn clone(&self) -> Self {
97        NodeDataRef {
98            _keep_alive: self._keep_alive.clone(),
99            _reference: self._reference,
100        }
101    }
102}
103
104impl<T: fmt::Debug> fmt::Debug for NodeDataRef<T> {
105    #[inline]
106    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
107        fmt::Debug::fmt(&**self, f)
108    }
109}
110
111impl NodeDataRef<ElementData> {
112    /// Return the concatenation of all text nodes in this subtree.
113    pub fn text_contents(&self) -> String {
114        self.as_node().text_contents()
115    }
116}