xot/
creation.rs

1use crate::error::Error;
2use crate::id::NameId;
3use crate::xmlvalue::{Attribute, Comment, Element, Namespace, ProcessingInstruction, Text, Value};
4use crate::xotdata::{Node, Xot};
5use crate::{NamespaceId, PrefixId};
6
7/// ## Creation
8///
9/// These are functions to help create nodes.
10///
11/// See also the convenience manipulation methods like [`Xot::append_element`]
12/// in the manipulation section.
13impl Xot {
14    pub(crate) fn new_node(&mut self, value: Value) -> Node {
15        Node::new(self.arena.new_node(value))
16    }
17
18    /// Create a new, unattached document node without document element.
19    ///
20    /// You can use this to create a new document from scratch.
21    /// If you don't attach at a single element later, the document
22    /// is going to be invalid.
23    ///
24    /// ```rust
25    /// use xot::Xot;
26    ///
27    /// let mut xot = Xot::new();
28    /// let doc_name = xot.add_name("doc");
29    /// let doc_el = xot.new_element(doc_name);
30    /// let txt = xot.new_text("Hello, world!");
31    /// xot.append(doc_el, txt)?;
32    ///
33    /// /// now create the document
34    /// let document = xot.new_document();
35    /// xot.append(document, doc_el)?;
36    ///
37    /// assert_eq!(xot.to_string(document)?, "<doc>Hello, world!</doc>");
38    /// # Ok::<(), xot::Error>(())
39    /// ```
40    pub fn new_document(&mut self) -> Node {
41        let root = Value::Document;
42        self.new_node(root)
43    }
44
45    /// Create a new document node with a document element.
46    ///
47    /// You can use this to create a new document from scratch. You have to
48    /// supply a document element. If you want to create an empty document node,
49    /// use `Xot::new_document`.
50    ///
51    /// ```rust
52    /// use xot::Xot;
53    ///
54    /// let mut xot = Xot::new();
55    /// let doc_name = xot.add_name("doc");
56    /// let doc_el = xot.new_element(doc_name);
57    /// let txt = xot.new_text("Hello, world!");
58    /// xot.append(doc_el, txt)?;
59    ///
60    /// /// now create the document
61    /// let document = xot.new_document_with_element(doc_el)?;
62    ///
63    /// assert_eq!(xot.to_string(document)?, "<doc>Hello, world!</doc>");
64    /// # Ok::<(), xot::Error>(())
65    /// ```
66    pub fn new_document_with_element(&mut self, node: Node) -> Result<Node, Error> {
67        if !self.is_element(node) {
68            return Err(Error::InvalidOperation(
69                "You must supply an element node".to_string(),
70            ));
71        }
72        let document_node = self.new_document();
73        self.append(document_node, node)?;
74        Ok(document_node)
75    }
76
77    /// Create a new, unattached element node given element name.
78    ///
79    /// You supply a [`crate::NameId`] or a [`crate::xmlname`] structure that
80    /// can be turned into a name id.
81    ///
82    /// To create a potentially new name you can use [`Xot::add_name`] or
83    /// [`Xot::add_name_ns`]. If the name already exists the existing name id
84    /// is returned.
85    ///
86    /// To reuse an existing name that has been previously used, you can use
87    /// [`Xot::name`] or [`Xot::name_ns`].
88    ///
89    /// ```rust
90    /// use xot::Xot;
91    ///
92    /// let mut xot = Xot::new();
93    /// let doc_name = xot.add_name("doc");
94    /// let doc_el = xot.new_element(doc_name);
95    ///
96    /// let root = xot.new_document_with_element(doc_el)?;
97    /// assert_eq!(xot.to_string(root)?, "<doc/>");
98    /// # Ok::<(), xot::Error>(())
99    /// ```
100    ///
101    /// With a namespaced element:
102    ///
103    /// ```rust
104    /// use xot::Xot;
105    ///
106    /// let mut xot = Xot::new();
107    /// let ns = xot.add_namespace("http://example.com");
108    /// let ex = xot.add_prefix("ex");
109    ///
110    /// // create name in namespace
111    /// let doc_name = xot.add_name_ns("doc", ns);
112    /// let doc_el = xot.new_element(doc_name);
113    ///
114    /// // set up namepace prefix for element so it serializes to XML nicely
115    /// xot.namespaces_mut(doc_el).insert(ex, ns);
116    ///
117    /// let root = xot.new_document_with_element(doc_el)?;
118    ///
119    /// assert_eq!(xot.to_string(root)?, r#"<ex:doc xmlns:ex="http://example.com"/>"#);
120    /// # Ok::<(), xot::Error>(())
121    /// ```
122    ///
123    /// Or with `xmlname`:
124    ///
125    ///```
126    /// use xot::{Xot, xmlname};
127    ///
128    /// let mut xot = Xot::new();
129    ///
130    /// let namespace = xmlname::CreateNamespace::new(&mut xot, "ex", "http://example.com");
131    /// let doc_name = xmlname::CreateName::namespaced(&mut xot, "doc", &namespace);
132    ///
133    /// let doc_el = xot.new_element(doc_name);
134    ///
135    /// // set up namepace prefix for element so it serializes to XML nicely
136    /// xot.append_namespace(doc_el, &namespace);
137    ///
138    /// let root = xot.new_document_with_element(doc_el)?;
139    ///
140    /// assert_eq!(xot.to_string(root)?, r#"<ex:doc xmlns:ex="http://example.com"/>"#);
141    ///
142    /// # Ok::<(), xot::Error>(())
143    /// ```
144    pub fn new_element(&mut self, name: impl Into<NameId>) -> Node {
145        let element = Value::Element(Element::new(name.into()));
146        self.new_node(element)
147    }
148
149    /// Create a new, unattached text node.
150    ///
151    /// ```rust
152    /// use xot::Xot;
153    ///
154    /// let mut xot = Xot::new();
155    /// let root = xot.parse(r#"<doc/>"#)?;
156    /// let doc_el = xot.document_element(root)?;
157    /// let txt = xot.new_text("Hello, world!");
158    /// xot.append(doc_el, txt)?;
159    /// assert_eq!(xot.to_string(root)?, "<doc>Hello, world!</doc>");
160    /// # Ok::<(), xot::Error>(())
161    /// ```
162    pub fn new_text(&mut self, text: &str) -> Node {
163        let text = Value::Text(Text::new(text.to_string()));
164        self.new_node(text)
165    }
166
167    /// Create a new, unattached comment node given comment text.
168    ///
169    /// ```rust
170    /// use xot::Xot;
171    ///
172    /// let mut xot = Xot::new();
173    /// let root = xot.parse(r#"<doc/>"#)?;
174    /// let doc_el = xot.document_element(root)?;
175    /// let comment = xot.new_comment("Hello, world!");
176    /// xot.append(doc_el, comment)?;
177    /// assert_eq!(xot.to_string(root)?, "<doc><!--Hello, world!--></doc>");
178    /// # Ok::<(), xot::Error>(())
179    /// ```
180    pub fn new_comment(&mut self, comment: &str) -> Node {
181        let comment = Value::Comment(Comment::new(comment.to_string()));
182        self.new_node(comment)
183    }
184
185    /// Create a new, unattached processing instruction.
186    ///
187    /// ```rust
188    /// use xot::Xot;
189    ///
190    /// let mut xot = Xot::new();
191    /// let target = xot.add_name("target");
192    /// let root = xot.parse(r#"<doc/>"#)?;
193    /// let doc_el = xot.document_element(root)?;
194    /// let pi = xot.new_processing_instruction(target, Some("data"));
195    /// xot.append(doc_el, pi)?;
196    /// assert_eq!(xot.to_string(root)?, r#"<doc><?target data?></doc>"#);
197    /// # Ok::<(), xot::Error>(())
198    /// ```
199    pub fn new_processing_instruction(
200        &mut self,
201        target: impl Into<NameId>,
202        data: Option<&str>,
203    ) -> Node {
204        let pi = Value::ProcessingInstruction(ProcessingInstruction::new(
205            target.into(),
206            data.map(|s| s.to_string()),
207        ));
208        self.new_node(pi)
209    }
210
211    /// Create a new, unattached attribute node.
212    ///
213    /// You can then use [`Xot::append_attribute_node`] to add it to an element node.
214    ///
215    /// This method is useful in situations where attributes need to be created
216    /// independently of elements, but for many use cases you can use the
217    /// [`Xot::attributes_mut`] API to create attributes instead.
218    ///
219    /// ```rust
220    /// use xot::Xot;
221    ///
222    /// let mut xot = Xot::new();
223    /// let foo = xot.add_name("foo");
224    /// let root = xot.parse(r#"<doc/>"#)?;
225    /// let doc_el = xot.document_element(root)?;
226    /// let attr = xot.new_attribute_node(foo, "FOO".to_string());
227    /// xot.append_attribute_node(doc_el, attr)?;
228    /// assert_eq!(xot.to_string(root)?, r#"<doc foo="FOO"/>"#);
229    /// # Ok::<(), xot::Error>(())
230    /// ```
231    pub fn new_attribute_node(&mut self, name: impl Into<NameId>, value: String) -> Node {
232        let attr = Value::Attribute(Attribute {
233            name_id: name.into(),
234            value,
235        });
236        self.new_node(attr)
237    }
238
239    /// Create a new, unattached namespace declaration node.
240    ///
241    /// You can then use [`Xot::append_namespace_node`] to add it to an element
242    /// node.
243    ///
244    /// This method is useful in situations where namespaces need to be created
245    /// independently of elements, but for many use cases you can use the
246    /// [`Xot::namespaces_mut`] API to create namespaces instead.
247    ///
248    /// ```rust
249    /// use xot::Xot;
250    ///
251    /// let mut xot = Xot::new();
252    /// let foo = xot.add_prefix("foo");
253    /// let ns = xot.add_namespace("http://example.com");
254    /// let root = xot.parse(r#"<doc/>"#)?;
255    /// let doc_el = xot.document_element(root)?;
256    /// let ns = xot.new_namespace_node(foo, ns);
257    /// xot.append_namespace_node(doc_el, ns)?;
258    /// assert_eq!(xot.to_string(root)?, r#"<doc xmlns:foo="http://example.com"/>"#);
259    ///
260    /// # Ok::<(), xot::Error>(())
261    /// ```
262    pub fn new_namespace_node(&mut self, prefix: PrefixId, namespace: NamespaceId) -> Node {
263        let ns = Value::Namespace(Namespace {
264            prefix_id: prefix,
265            namespace_id: namespace,
266        });
267        self.new_node(ns)
268    }
269}