Skip to main content

afia_component/dom/
node.rs

1//! Types related to DOM nodes.
2//!
3//! ## MDN Documentation
4//! https://developer.mozilla.org/en-US/docs/Web/API/Node
5
6use afia_component_sys::{node_type, parent_node};
7
8use crate::dom::element::DomElement;
9use crate::ComponentImports;
10
11/// A DOM node.
12///
13/// ## MDN Documentation
14/// https://developer.mozilla.org/en-US/docs/Web/API/Node
15pub struct DomNode {
16    handle: i64,
17    imports: ComponentImports,
18}
19
20/// Behavior related to DOM nodes.
21pub trait AsDomNode: impls::Sealed {
22    #[doc(hidden)]
23    fn handle(&self) -> i64;
24
25    #[doc(hidden)]
26    fn imports(&self) -> &ComponentImports;
27    #[doc(hidden)]
28    fn imports_ptr(&self) -> *const std::ffi::c_void {
29        self.imports().component_imports_ptr
30    }
31
32    /// Convert this type into a [`DomNode`].
33    fn to_dom_node(&self) -> DomNode {
34        DomNode {
35            handle: self.handle(),
36            imports: self.imports().clone(),
37        }
38    }
39
40    /// Get the Node's type.
41    fn node_type(&self) -> NodeType {
42        let node_type = unsafe { node_type(self.imports_ptr(), self.handle()) };
43        NodeType::from(node_type)
44    }
45
46    /// Get the Node's parent.
47    fn parent_node(&self) -> Option<DomNode> {
48        let parent = unsafe { parent_node(self.imports_ptr(), self.handle()) };
49        maybe_node(self.imports(), parent)
50    }
51}
52
53/// A DOM node's type
54///
55/// ## MDN Documentation
56/// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
57#[allow(missing_docs)]
58pub enum NodeType {
59    Element = 1,
60    Text = 3,
61}
62
63impl From<i32> for NodeType {
64    fn from(value: i32) -> Self {
65        match value {
66            1 => Self::Element,
67            3 => Self::Text,
68            _ => unimplemented!(),
69        }
70    }
71}
72
73impl DomNode {
74    pub(crate) fn new_maybe(imports: &ComponentImports, node_handle: i64) -> Option<DomNode> {
75        maybe_node(imports, node_handle)
76    }
77
78    /// Checks if this node is an element and, if so, returns the element.
79    pub fn to_element(&self) -> Option<DomElement> {
80        if matches!(self.node_type(), NodeType::Element) {
81            let elem = DomElement::maybe_new(self.handle, self.imports.clone()).unwrap();
82            Some(elem)
83        } else {
84            None
85        }
86    }
87}
88
89fn maybe_node(imports: &ComponentImports, node_handle: i64) -> Option<DomNode> {
90    if node_handle == 0 {
91        None
92    } else {
93        Some(DomNode {
94            imports: imports.clone(),
95            handle: node_handle,
96        })
97    }
98}
99
100mod impls {
101    use super::*;
102    use crate::dom::element::DomElement;
103    use crate::dom::text::DomText;
104
105    pub trait Sealed {}
106
107    impl AsDomNode for DomNode {
108        fn handle(&self) -> i64 {
109            self.handle
110        }
111
112        fn imports(&self) -> &ComponentImports {
113            &self.imports
114        }
115    }
116    impl Sealed for DomNode {}
117
118    impl AsDomNode for DomElement {
119        fn handle(&self) -> i64 {
120            self.to_i64()
121        }
122
123        fn imports(&self) -> &ComponentImports {
124            self.component_imports()
125        }
126    }
127    impl Sealed for DomElement {}
128
129    impl AsDomNode for DomText {
130        fn handle(&self) -> i64 {
131            self.to_i64()
132        }
133
134        fn imports(&self) -> &ComponentImports {
135            self.component_imports()
136        }
137    }
138    impl Sealed for DomText {}
139}