libxml/tree/
node.rs

1//! Node, and related, feature set
2//!
3use libc::{c_char, c_void};
4use std::cell::RefCell;
5use std::collections::{HashMap, HashSet};
6use std::error::Error;
7use std::ffi::{CStr, CString};
8use std::hash::{Hash, Hasher};
9use std::ptr;
10use std::rc::Rc;
11use std::str;
12
13use crate::bindings::*;
14use crate::c_helpers::*;
15use crate::tree::namespace::Namespace;
16use crate::tree::nodetype::NodeType;
17use crate::tree::{Document, DocumentRef, DocumentWeak};
18use crate::xpath::Context;
19
20/// Guard treshold for enforcing runtime mutability checks for Nodes
21pub static mut NODE_RC_MAX_GUARD: usize = 2;
22
23/// Set the guard value for the max Rc "strong count" allowed for mutable use of a Node
24/// Default is 2
25pub fn set_node_rc_guard(value: usize) {
26  unsafe {
27    NODE_RC_MAX_GUARD = value;
28  }
29}
30
31type NodeRef = Rc<RefCell<_Node>>;
32
33#[derive(Debug)]
34struct _Node {
35  /// libxml's xmlNodePtr
36  node_ptr: xmlNodePtr,
37  /// Reference to parent `Document`
38  document: DocumentWeak,
39  /// Bookkeep removal from a parent
40  unlinked: bool,
41}
42
43/// An xml node
44#[derive(Clone, Debug)]
45pub struct Node(NodeRef);
46
47impl Hash for Node {
48  /// Generates a hash value from the `node_ptr` value.
49  fn hash<H: Hasher>(&self, state: &mut H) {
50    self.node_ptr().hash(state);
51  }
52}
53
54impl PartialEq for Node {
55  /// Two nodes are considered equal, if they point to the same xmlNode.
56  fn eq(&self, other: &Node) -> bool {
57    std::ptr::eq(self.node_ptr(), other.node_ptr())
58  }
59}
60
61impl Eq for Node {}
62
63impl Drop for _Node {
64  /// Free node if it isn't bound in some document
65  /// Warning: xmlFreeNode is RECURSIVE into the node's children, so this may lead to segfaults if used carelessly
66  fn drop(&mut self) {
67    if self.unlinked {
68      let node_ptr = self.node_ptr;
69      if !node_ptr.is_null() {
70        unsafe {
71          xmlFreeNode(node_ptr);
72        }
73      }
74    }
75  }
76}
77
78impl Node {
79  /// Create a new node, bound to a given document.
80  pub fn new(name: &str, ns: Option<Namespace>, doc: &Document) -> Result<Self, ()> {
81    // We will only allow to work with document-bound nodes for now, to avoid the problems of memory management.
82
83    let c_name = CString::new(name).unwrap();
84    let ns_ptr = match ns {
85      None => ptr::null_mut(),
86      Some(ns) => ns.ns_ptr(),
87    };
88    unsafe {
89      let node = xmlNewDocRawNode(
90        doc.doc_ptr(),
91        ns_ptr,
92        c_name.as_bytes().as_ptr(),
93        ptr::null(),
94      );
95      if node.is_null() {
96        Err(())
97      } else {
98        Ok(Node::wrap_new(node, &doc.0))
99      }
100    }
101  }
102
103  /// Immutably borrows the underlying libxml2 `xmlNodePtr` pointer
104  pub fn node_ptr(&self) -> xmlNodePtr {
105    self.0.borrow().node_ptr
106  }
107
108  /// Mutably borrows the underlying libxml2 `xmlNodePtr` pointer
109  /// Also protects against mutability conflicts at runtime.
110  pub fn node_ptr_mut(&mut self) -> Result<xmlNodePtr, String> {
111    let weak_count = Rc::weak_count(&self.0);
112    let strong_count = Rc::strong_count(&self.0);
113
114    // The basic idea would be to use `Rc::get_mut` to guard against multiple borrows.
115    // However, our approach to bookkeeping nodes implies there is *always* a second Rc reference
116    // in the document.nodes Hash. So rather than use `get_mut` directly, the
117    // correct check would be to have a weak count of 0 and a strong count <=2 (one for self, one for .nodes)
118    let guard_ok = unsafe { weak_count == 0 && strong_count <= NODE_RC_MAX_GUARD };
119    if guard_ok {
120      Ok(self.0.borrow_mut().node_ptr)
121    } else {
122      Err(format!(
123        "Can not mutably reference a shared Node {:?}! Rc: weak count: {:?}; strong count: {:?}",
124        self.get_name(),
125        weak_count,
126        strong_count,
127      ))
128    }
129  }
130
131  /// Wrap a libxml node ptr with a Node
132  fn _wrap(node_ptr: xmlNodePtr, unlinked: bool, document: &DocumentRef) -> Node {
133    // If already seen, return saved Node
134    if let Some(node) = document.borrow().get_node(node_ptr) {
135      return node.clone();
136    }
137    // If newly encountered pointer, wrap
138    let node = _Node {
139      node_ptr,
140      document: Rc::downgrade(document),
141      unlinked,
142    };
143    let wrapped_node = Node(Rc::new(RefCell::new(node)));
144    document
145      .borrow_mut()
146      .insert_node(node_ptr, wrapped_node.clone());
147    wrapped_node
148  }
149  /// Wrap a node already linked to a `document` tree
150  pub(crate) fn wrap(node_ptr: xmlNodePtr, document: &DocumentRef) -> Node {
151    Node::_wrap(node_ptr, false, document)
152  }
153  /// Wrap, a node owned by, but not yet linked to, a `document`
154  pub(crate) fn wrap_new(node_ptr: xmlNodePtr, document: &DocumentRef) -> Node {
155    Node::_wrap(node_ptr, true, document)
156  }
157
158  /// Create a new text node, bound to a given document
159  pub fn new_text(content: &str, doc: &Document) -> Result<Self, ()> {
160    // We will only allow to work with document-bound nodes for now, to avoid the problems of memory management.
161    let c_content = CString::new(content).unwrap();
162    unsafe {
163      let node = xmlNewDocText(doc.doc_ptr(), c_content.as_bytes().as_ptr());
164      if node.is_null() {
165        Err(())
166      } else {
167        Ok(Node::wrap_new(node, &doc.0))
168      }
169    }
170  }
171  /// Create a mock node, used for a placeholder argument
172  pub fn mock(doc: &Document) -> Self {
173    Node::new("mock", None, doc).unwrap()
174  }
175
176  /// Create a mock node, used for a placeholder argument
177  pub fn null() -> Self {
178    Node(Rc::new(RefCell::new(_Node {
179      node_ptr: ptr::null_mut(),
180      document: Rc::downgrade(&Document::null_ref()),
181      unlinked: true,
182    })))
183  }
184
185  /// `libc::c_void` isn't hashable and cannot be made hashable
186  pub fn to_hashable(&self) -> usize {
187    self.node_ptr() as usize
188  }
189
190  pub(crate) fn get_docref(&self) -> DocumentWeak {
191    self.0.borrow().document.clone()
192  }
193
194  /// Returns the next sibling if it exists
195  pub fn get_next_sibling(&self) -> Option<Node> {
196    let ptr = xmlNextSibling(self.node_ptr());
197    self.ptr_as_option(ptr)
198  }
199
200  /// Returns the previous sibling if it exists
201  pub fn get_prev_sibling(&self) -> Option<Node> {
202    let ptr = xmlPrevSibling(self.node_ptr());
203    self.ptr_as_option(ptr)
204  }
205
206  /// Returns the first child if it exists
207  pub fn get_first_child(&self) -> Option<Node> {
208    let ptr = xmlGetFirstChild(self.node_ptr());
209    self.ptr_as_option(ptr)
210  }
211  /// Returns the last child if it exists
212  pub fn get_last_child(&self) -> Option<Node> {
213    let ptr = unsafe { xmlGetLastChild(self.node_ptr()) };
214    self.ptr_as_option(ptr)
215  }
216
217  /// Returns the next element sibling if it exists
218  pub fn get_next_element_sibling(&self) -> Option<Node> {
219    match self.get_next_sibling() {
220      None => None,
221      Some(child) => {
222        let mut current_node = child;
223        while !current_node.is_element_node() {
224          if let Some(sibling) = current_node.get_next_sibling() {
225            current_node = sibling;
226          } else {
227            break;
228          }
229        }
230        if current_node.is_element_node() {
231          Some(current_node)
232        } else {
233          None
234        }
235      }
236    }
237  }
238
239  /// Returns the previous element sibling if it exists
240  pub fn get_prev_element_sibling(&self) -> Option<Node> {
241    match self.get_prev_sibling() {
242      None => None,
243      Some(child) => {
244        let mut current_node = child;
245        while !current_node.is_element_node() {
246          if let Some(sibling) = current_node.get_prev_sibling() {
247            current_node = sibling;
248          } else {
249            break;
250          }
251        }
252        if current_node.is_element_node() {
253          Some(current_node)
254        } else {
255          None
256        }
257      }
258    }
259  }
260
261  /// Returns the first element child if it exists
262  pub fn get_first_element_child(&self) -> Option<Node> {
263    match self.get_first_child() {
264      None => None,
265      Some(child) => {
266        let mut current_node = child;
267        while !current_node.is_element_node() {
268          if let Some(sibling) = current_node.get_next_sibling() {
269            current_node = sibling;
270          } else {
271            break;
272          }
273        }
274        if current_node.is_element_node() {
275          Some(current_node)
276        } else {
277          None
278        }
279      }
280    }
281  }
282
283  /// Returns the last element child if it exists
284  pub fn get_last_element_child(&self) -> Option<Node> {
285    match self.get_last_child() {
286      None => None,
287      Some(child) => {
288        let mut current_node = child;
289        while !current_node.is_element_node() {
290          if let Some(sibling) = current_node.get_prev_sibling() {
291            current_node = sibling;
292          } else {
293            break;
294          }
295        }
296        if current_node.is_element_node() {
297          Some(current_node)
298        } else {
299          None
300        }
301      }
302    }
303  }
304
305  /// Returns all child nodes of the given node as a vector
306  pub fn get_child_nodes(&self) -> Vec<Node> {
307    let mut children = Vec::new();
308    if let Some(first_child) = self.get_first_child() {
309      children.push(first_child);
310      while let Some(sibling) = children.last().unwrap().get_next_sibling() {
311        children.push(sibling)
312      }
313    }
314    children
315  }
316
317  /// Returns all child elements of the given node as a vector
318  pub fn get_child_elements(&self) -> Vec<Node> {
319    self
320      .get_child_nodes()
321      .into_iter()
322      .filter(|n| n.get_type() == Some(NodeType::ElementNode))
323      .collect::<Vec<Node>>()
324  }
325
326  /// Returns the parent if it exists
327  pub fn get_parent(&self) -> Option<Node> {
328    let ptr = xmlGetParent(self.node_ptr());
329    self.ptr_as_option(ptr)
330  }
331
332  /// Get the node type
333  pub fn get_type(&self) -> Option<NodeType> {
334    NodeType::from_int(xmlGetNodeType(self.node_ptr()))
335  }
336
337  /// Add a previous sibling
338  pub fn add_prev_sibling(
339    &mut self,
340    new_sibling: &mut Node,
341  ) -> Result<(), Box<dyn Error + Send + Sync>> {
342    new_sibling.set_linked();
343    unsafe {
344      if xmlAddPrevSibling(self.node_ptr_mut()?, new_sibling.node_ptr_mut()?).is_null() {
345        Err(From::from("add_prev_sibling returned NULL"))
346      } else {
347        Ok(())
348      }
349    }
350  }
351
352  /// Add a next sibling
353  pub fn add_next_sibling(
354    &mut self,
355    new_sibling: &mut Node,
356  ) -> Result<(), Box<dyn Error + Send + Sync>> {
357    new_sibling.set_linked();
358    unsafe {
359      if xmlAddNextSibling(self.node_ptr_mut()?, new_sibling.node_ptr_mut()?).is_null() {
360        Err(From::from("add_next_sibling returned NULL"))
361      } else {
362        Ok(())
363      }
364    }
365  }
366
367  /// Returns true if it is a text node
368  pub fn is_text_node(&self) -> bool {
369    self.get_type() == Some(NodeType::TextNode)
370  }
371
372  /// Checks if the given node is an Element
373  pub fn is_element_node(&self) -> bool {
374    self.get_type() == Some(NodeType::ElementNode)
375  }
376
377  /// Checks if the underlying libxml2 pointer is `NULL`
378  pub fn is_null(&self) -> bool {
379    self.node_ptr().is_null()
380  }
381
382  /// Returns the name of the node (empty string if name pointer is `NULL`)
383  pub fn get_name(&self) -> String {
384    let name_ptr = xmlNodeGetName(self.node_ptr());
385    if name_ptr.is_null() {
386      return String::new();
387    } //empty string
388    let c_string = unsafe { CStr::from_ptr(name_ptr) };
389    c_string.to_string_lossy().into_owned()
390  }
391
392  /// Sets the name of this `Node`
393  pub fn set_name(&mut self, name: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
394    let c_name = CString::new(name).unwrap();
395    unsafe { xmlNodeSetName(self.node_ptr_mut()?, c_name.as_bytes().as_ptr()) }
396    Ok(())
397  }
398
399  /// Returns the content of the node
400  /// (assumes UTF-8 XML document)
401  pub fn get_content(&self) -> String {
402    let content_ptr = unsafe { xmlNodeGetContent(self.node_ptr()) };
403    if content_ptr.is_null() {
404      //empty string when none
405      return String::new();
406    }
407    let c_string = unsafe { CStr::from_ptr(content_ptr as *const c_char) };
408    let rust_utf8 = c_string.to_string_lossy().into_owned();
409    bindgenFree(content_ptr as *mut c_void);
410    rust_utf8
411  }
412
413  /// Sets the text content of this `Node`
414  pub fn set_content(&mut self, content: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
415    let c_content = CString::new(content).unwrap();
416    unsafe { xmlNodeSetContent(self.node_ptr_mut()?, c_content.as_bytes().as_ptr()); }
417    Ok(())
418  }
419
420  /// Returns the value of property `name`
421  pub fn get_property(&self, name: &str) -> Option<String> {
422    let c_name = CString::new(name).unwrap();
423    let value_ptr = unsafe { xmlGetProp(self.node_ptr(), c_name.as_bytes().as_ptr()) };
424    if value_ptr.is_null() {
425      return None;
426    }
427    let c_value_string = unsafe { CStr::from_ptr(value_ptr as *const c_char) };
428    let prop_str = c_value_string.to_string_lossy().into_owned();
429    bindgenFree(value_ptr as *mut c_void);
430    Some(prop_str)
431  }
432
433  /// Returns the value of property `name` in namespace `ns`
434  pub fn get_property_ns(&self, name: &str, ns: &str) -> Option<String> {
435    let c_name = CString::new(name).unwrap();
436    let c_ns = CString::new(ns).unwrap();
437    let value_ptr = unsafe {
438      xmlGetNsProp(
439        self.node_ptr(),
440        c_name.as_bytes().as_ptr(),
441        c_ns.as_bytes().as_ptr(),
442      )
443    };
444    if value_ptr.is_null() {
445      return None;
446    }
447    let c_value_string = unsafe { CStr::from_ptr(value_ptr as *const c_char) };
448    let prop_str = c_value_string.to_string_lossy().into_owned();
449    bindgenFree(value_ptr as *mut c_void);
450    Some(prop_str)
451  }
452
453  /// Returns the value of property `name` with no namespace
454  pub fn get_property_no_ns(&self, name: &str) -> Option<String> {
455    let c_name = CString::new(name).unwrap();
456    let value_ptr = unsafe { xmlGetNoNsProp(self.node_ptr(), c_name.as_bytes().as_ptr()) };
457    if value_ptr.is_null() {
458      return None;
459    }
460    let c_value_string = unsafe { CStr::from_ptr(value_ptr as *const c_char) };
461    let prop_str = c_value_string.to_string_lossy().into_owned();
462    bindgenFree(value_ptr as *mut c_void);
463    Some(prop_str)
464  }
465
466  /// Return an attribute as a `Node` struct of type AttributeNode
467  pub fn get_property_node(&self, name: &str) -> Option<Node> {
468    let c_name = CString::new(name).unwrap();
469    unsafe {
470      let attr_node = xmlHasProp(self.node_ptr(), c_name.as_bytes().as_ptr());
471      self.ptr_as_option(attr_node as xmlNodePtr)
472    }
473  }
474
475  /// Return an attribute in a namespace `ns` as a `Node` of type AttributeNode
476  pub fn get_property_node_ns(&self, name: &str, ns: &str) -> Option<Node> {
477    let c_name = CString::new(name).unwrap();
478    let c_ns = CString::new(ns).unwrap();
479    let attr_node = unsafe {
480      xmlHasNsProp(
481        self.node_ptr(),
482        c_name.as_bytes().as_ptr(),
483        c_ns.as_bytes().as_ptr(),
484      )
485    };
486    self.ptr_as_option(attr_node as xmlNodePtr)
487  }
488
489  /// Return an attribute with no namespace as a `Node` of type AttributeNode
490  pub fn get_property_node_no_ns(&self, name: &str) -> Option<Node> {
491    let c_name = CString::new(name).unwrap();
492    let attr_node =
493      unsafe { xmlHasNsProp(self.node_ptr(), c_name.as_bytes().as_ptr(), ptr::null()) };
494    self.ptr_as_option(attr_node as xmlNodePtr)
495  }
496
497  /// Check if a property has been defined, without allocating its value
498  pub fn has_property(&self, name: &str) -> bool {
499    let c_name = CString::new(name).unwrap();
500    let value_ptr = unsafe { xmlHasProp(self.node_ptr(), c_name.as_bytes().as_ptr()) };
501    !value_ptr.is_null()
502  }
503
504  /// Check if property `name` in namespace `ns` exists
505  pub fn has_property_ns(&self, name: &str, ns: &str) -> bool {
506    let c_name = CString::new(name).unwrap();
507    let c_ns = CString::new(ns).unwrap();
508    let value_ptr = unsafe {
509      xmlHasNsProp(
510        self.node_ptr(),
511        c_name.as_bytes().as_ptr(),
512        c_ns.as_bytes().as_ptr(),
513      )
514    };
515    !value_ptr.is_null()
516  }
517
518  /// Check if property `name` with no namespace exists
519  pub fn has_property_no_ns(&self, name: &str) -> bool {
520    let c_name = CString::new(name).unwrap();
521    let value_ptr =
522      unsafe { xmlHasNsProp(self.node_ptr(), c_name.as_bytes().as_ptr(), ptr::null()) };
523    !value_ptr.is_null()
524  }
525
526  /// Alias for has_property
527  pub fn has_attribute(&self, name: &str) -> bool {
528    self.has_property(name)
529  }
530  /// Alias for has_property_ns
531  pub fn has_attribute_ns(&self, name: &str, ns: &str) -> bool {
532    self.has_property_ns(name, ns)
533  }
534
535  /// Alias for has_property_no_ns
536  pub fn has_attribute_no_ns(&self, name: &str) -> bool {
537    self.has_property_no_ns(name)
538  }
539
540  /// Sets the value of property `name` to `value`
541  pub fn set_property(
542    &mut self,
543    name: &str,
544    value: &str,
545  ) -> Result<(), Box<dyn Error + Send + Sync>> {
546    let c_name = CString::new(name).unwrap();
547    let c_value = CString::new(value).unwrap();
548    unsafe {
549      xmlSetProp(
550        self.node_ptr_mut()?,
551        c_name.as_bytes().as_ptr(),
552        c_value.as_bytes().as_ptr(),
553      )
554    };
555    Ok(())
556  }
557  /// Sets a namespaced attribute
558  pub fn set_property_ns(
559    &mut self,
560    name: &str,
561    value: &str,
562    ns: &Namespace,
563  ) -> Result<(), Box<dyn Error + Send + Sync>> {
564    let c_name = CString::new(name).unwrap();
565    let c_value = CString::new(value).unwrap();
566    unsafe {
567      xmlSetNsProp(
568        self.node_ptr_mut()?,
569        ns.ns_ptr(),
570        c_name.as_bytes().as_ptr(),
571        c_value.as_bytes().as_ptr(),
572      )
573    };
574    Ok(())
575  }
576
577  /// Removes the property of given `name`
578  pub fn remove_property(&mut self, name: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
579    let c_name = CString::new(name).unwrap();
580    unsafe {
581      let attr_node = xmlHasProp(self.node_ptr_mut()?, c_name.as_bytes().as_ptr());
582      if !attr_node.is_null() {
583        let remove_prop_status = xmlRemoveProp(attr_node);
584        if remove_prop_status == 0 {
585          Ok(())
586        } else {
587          // Propagate libxml2 failure to remove
588          Err(From::from(format!(
589            "libxml2 failed to remove property with status: {remove_prop_status:?}")))
590        }
591      } else {
592        // silently no-op if asked to remove a property which is not present
593        Ok(())
594      }
595    }
596  }
597
598  /// Removes the property of given `name` and namespace (`ns`)
599  pub fn remove_property_ns(
600    &mut self,
601    name: &str,
602    ns: &str,
603  ) -> Result<(), Box<dyn Error + Send + Sync>> {
604    let c_name = CString::new(name).unwrap();
605    let c_ns = CString::new(ns).unwrap();
606    unsafe {
607      let attr_node = xmlHasNsProp(
608        self.node_ptr_mut()?,
609        c_name.as_bytes().as_ptr(),
610        c_ns.as_bytes().as_ptr(),
611      );
612      if !attr_node.is_null() {
613        let remove_prop_status = xmlRemoveProp(attr_node);
614        if remove_prop_status == 0 {
615          Ok(())
616        } else {
617          // Propagate libxml2 failure to remove
618          Err(From::from(format!(
619            "libxml2 failed to remove property with status: {remove_prop_status:?}")))
620        }
621      } else {
622        // silently no-op if asked to remove a property which is not present
623        Ok(())
624      }
625    }
626  }
627
628  /// Removes the property of given `name` with no namespace
629  pub fn remove_property_no_ns(&mut self, name: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
630    let c_name = CString::new(name).unwrap();
631    let attr_node = unsafe {
632      xmlHasNsProp(
633        self.node_ptr_mut()?,
634        c_name.as_bytes().as_ptr(),
635        ptr::null(),
636      )
637    };
638    if !attr_node.is_null() {
639      let remove_prop_status = unsafe { xmlRemoveProp(attr_node) };
640      if remove_prop_status == 0 {
641        Ok(())
642      } else {
643        // Propagate libxml2 failure to remove
644        Err(From::from(format!(
645          "libxml2 failed to remove property with status: {remove_prop_status:?}")))
646      }
647    } else {
648      // silently no-op if asked to remove a property which is not present
649      Ok(())
650    }
651  }
652
653  /// Alias for get_property
654  pub fn get_attribute(&self, name: &str) -> Option<String> {
655    self.get_property(name)
656  }
657
658  /// Alias for get_property_ns
659  pub fn get_attribute_ns(&self, name: &str, ns: &str) -> Option<String> {
660    self.get_property_ns(name, ns)
661  }
662
663  /// Alias for get_property_no_ns
664  pub fn get_attribute_no_ns(&self, name: &str) -> Option<String> {
665    self.get_property_no_ns(name)
666  }
667
668  /// Alias for get_property_node
669  pub fn get_attribute_node(&self, name: &str) -> Option<Node> {
670    self.get_property_node(name)
671  }
672
673  /// Alias for get_property_node_ns
674  pub fn get_attribute_node_ns(&self, name: &str, ns: &str) -> Option<Node> {
675    self.get_property_node_ns(name, ns)
676  }
677
678  /// Alias for get_property_node_no_ns
679  pub fn get_attribute_node_no_ns(&self, name: &str) -> Option<Node> {
680    self.get_property_node_no_ns(name)
681  }
682
683  /// Alias for set_property
684  pub fn set_attribute(
685    &mut self,
686    name: &str,
687    value: &str,
688  ) -> Result<(), Box<dyn Error + Send + Sync>> {
689    self.set_property(name, value)
690  }
691  /// Alias for set_property_ns
692  pub fn set_attribute_ns(
693    &mut self,
694    name: &str,
695    value: &str,
696    ns: &Namespace,
697  ) -> Result<(), Box<dyn Error + Send + Sync>> {
698    self.set_property_ns(name, value, ns)
699  }
700
701  /// Alias for remove_property
702  pub fn remove_attribute(&mut self, name: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
703    self.remove_property(name)
704  }
705
706  /// Alias for remove_property_ns
707  pub fn remove_attribute_ns(
708    &mut self,
709    name: &str,
710    ns: &str,
711  ) -> Result<(), Box<dyn Error + Send + Sync>> {
712    self.remove_property_ns(name, ns)
713  }
714
715  /// Alias for remove_property_no_ns
716  pub fn remove_attribute_no_ns(&mut self, name: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
717    self.remove_property_no_ns(name)
718  }
719
720  /// Get a copy of the attributes of this node
721  pub fn get_properties(&self) -> HashMap<String, String> {
722    let mut attributes = HashMap::new();
723
724    let mut current_prop = xmlGetFirstProperty(self.node_ptr());
725    while !current_prop.is_null() {
726      let name_ptr = xmlAttrName(current_prop);
727      let c_name_string = unsafe { CStr::from_ptr(name_ptr) };
728      let name = c_name_string.to_string_lossy().into_owned();
729      let value = self.get_property(&name).unwrap_or_default();
730      attributes.insert(name, value);
731      current_prop = xmlNextPropertySibling(current_prop);
732    }
733
734    attributes
735  }
736
737  /// Get a copy of this node's attributes and their namespaces
738  pub fn get_properties_ns(&self) -> HashMap<(String, Option<Namespace>), String> {
739    let mut attributes = HashMap::new();
740
741    let mut current_prop = xmlGetFirstProperty(self.node_ptr());
742    while !current_prop.is_null() {
743      let name_ptr = xmlAttrName(current_prop);
744      let c_name_string = unsafe { CStr::from_ptr(name_ptr) };
745      let name = c_name_string.to_string_lossy().into_owned();
746      let ns_ptr = xmlAttrNs(current_prop);
747      if ns_ptr.is_null() {
748        let value = self.get_property_no_ns(&name).unwrap_or_default();
749        attributes.insert((name, None), value);
750      } else {
751        let ns = Namespace { ns_ptr };
752        let value = self
753          .get_property_ns(&name, &ns.get_href())
754          .unwrap_or_default();
755        attributes.insert((name, Some(ns)), value);
756      }
757      current_prop = xmlNextPropertySibling(current_prop);
758    }
759
760    attributes
761  }
762
763  /// Alias for `get_properties`
764  pub fn get_attributes(&self) -> HashMap<String, String> {
765    self.get_properties()
766  }
767
768  /// Alias for `get_properties_ns`
769  pub fn get_attributes_ns(&self) -> HashMap<(String, Option<Namespace>), String> {
770    self.get_properties_ns()
771  }
772
773  /// Gets the active namespace associated of this node
774  pub fn get_namespace(&self) -> Option<Namespace> {
775    let ns_ptr = xmlNodeNs(self.node_ptr());
776    if ns_ptr.is_null() {
777      None
778    } else {
779      Some(Namespace { ns_ptr })
780    }
781  }
782
783  /// Gets a list of namespaces associated with this node
784  pub fn get_namespaces(&self, doc: &Document) -> Vec<Namespace> {
785    let list_ptr_raw = unsafe { xmlGetNsList(doc.doc_ptr(), self.node_ptr()) };
786    if list_ptr_raw.is_null() {
787      Vec::new()
788    } else {
789      let mut namespaces = Vec::new();
790      let mut ptr_iter = list_ptr_raw as *mut xmlNsPtr;
791      unsafe {
792        while !ptr_iter.is_null() && !(*ptr_iter).is_null() {
793          namespaces.push(Namespace { ns_ptr: *ptr_iter });
794          ptr_iter = ptr_iter.add(1);
795        }
796        /* TODO: valgrind suggests this technique isn't sufficiently fluent:
797          ==114895== Conditional jump or move depends on uninitialised value(s)
798          ==114895==    at 0x4E9962F: xmlFreeNs (in /usr/lib/x86_64-linux-gnu/libxml2.so.2.9.4)
799          ==114895==    by 0x195CE8: libxml::tree::Node::get_namespaces (tree.rs:723)
800          ==114895==    by 0x12E7B6: base_tests::can_work_with_namespaces (base_tests.rs:537)
801
802          DG: I could not improve on this state without creating memory leaks after ~1 hour, so I am
803          marking it as future work.
804        */
805        /* TODO: How do we properly deallocate here? The approach bellow reliably segfaults tree_tests on 1 thread */
806        // println!("\n-- xmlfreens on : {:?}", list_ptr_raw);
807        // xmlFreeNs(list_ptr_raw as xmlNsPtr);
808      }
809      namespaces
810    }
811  }
812
813  /// Get a list of namespaces declared with this node
814  pub fn get_namespace_declarations(&self) -> Vec<Namespace> {
815    if self.get_type() != Some(NodeType::ElementNode) {
816      // only element nodes can have declarations
817      return Vec::new();
818    }
819    let mut namespaces = Vec::new();
820    let mut ns_ptr = xmlNodeNsDeclarations(self.node_ptr());
821    while !ns_ptr.is_null() {
822      if !xmlNsPrefix(ns_ptr).is_null() || !xmlNsHref(ns_ptr).is_null() {
823        namespaces.push(Namespace { ns_ptr });
824      }
825      ns_ptr = xmlNextNsSibling(ns_ptr);
826    }
827    namespaces
828  }
829
830  /// Sets a `Namespace` for the node
831  pub fn set_namespace(
832    &mut self,
833    namespace: &Namespace,
834  ) -> Result<(), Box<dyn Error + Send + Sync>> {
835    unsafe {
836      xmlSetNs(self.node_ptr_mut()?, namespace.ns_ptr());
837    }
838    Ok(())
839  }
840
841  /// Looks up the prefix of a namespace from its URI, basedo around a given `Node`
842  pub fn lookup_namespace_prefix(&self, href: &str) -> Option<String> {
843    if href.is_empty() {
844      return None;
845    }
846    let c_href = CString::new(href).unwrap();
847    unsafe {
848      let ptr_mut = self.node_ptr();
849      let ns_ptr = xmlSearchNsByHref(xmlGetDoc(ptr_mut), ptr_mut, c_href.as_bytes().as_ptr());
850      if !ns_ptr.is_null() {
851        let ns = Namespace { ns_ptr };
852        let ns_prefix = ns.get_prefix();
853        Some(ns_prefix)
854      } else {
855        None
856      }
857    }
858  }
859
860  /// Looks up the uri of a namespace from its prefix, basedo around a given `Node`
861  pub fn lookup_namespace_uri(&self, prefix: &str) -> Option<String> {
862    if prefix.is_empty() {
863      return None;
864    }
865    let c_prefix = CString::new(prefix).unwrap();
866    unsafe {
867      let ns_ptr = xmlSearchNs(
868        xmlGetDoc(self.node_ptr()),
869        self.node_ptr(),
870        c_prefix.as_bytes().as_ptr(),
871      );
872      if !ns_ptr.is_null() {
873        let ns = Namespace { ns_ptr };
874        let ns_prefix = ns.get_href();
875        if !ns_prefix.is_empty() {
876          Some(ns_prefix)
877        } else {
878          None
879        }
880      } else {
881        None
882      }
883    }
884  }
885
886  // TODO: Clear a future Document namespaces vec
887  /// Removes the namespaces of this `Node` and it's children!
888  pub fn recursively_remove_namespaces(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
889    xmlNodeRecursivelyRemoveNs(self.node_ptr_mut()?);
890    Ok(())
891  }
892
893  /// Get a set of class names from this node's attributes
894  pub fn get_class_names(&self) -> HashSet<String> {
895    let mut set = HashSet::new();
896    if let Some(value) = self.get_property("class") {
897      for n in value.split(' ') {
898        set.insert(n.to_owned());
899      }
900    }
901    set
902  }
903
904  /// Creates a new `Node` as child to the self `Node`
905  pub fn add_child(&mut self, child: &mut Node) -> Result<(), String> {
906    child.set_linked();
907    unsafe {
908      let new_child_ptr = xmlAddChild(self.node_ptr_mut()?, child.node_ptr_mut()?);
909      if new_child_ptr.is_null() {
910        Err("add_child encountered NULL pointer".to_string())
911      } else {
912        Ok(())
913      }
914    }
915  }
916
917  /// Creates a new `Node` as child to the self `Node`
918  pub fn new_child(
919    &mut self,
920    ns: Option<Namespace>,
921    name: &str,
922  ) -> Result<Node, Box<dyn Error + Send + Sync>> {
923    let c_name = CString::new(name).unwrap();
924    let ns_ptr = match ns {
925      None => ptr::null_mut(),
926      Some(mut ns) => ns.ns_ptr_mut(),
927    };
928    unsafe {
929      let new_ptr = xmlNewChild(
930        self.node_ptr_mut()?,
931        ns_ptr,
932        c_name.as_bytes().as_ptr(),
933        ptr::null(),
934      );
935      Ok(Node::wrap(new_ptr, &self.get_docref().upgrade().unwrap()))
936    }
937  }
938
939  /// Adds a new text child, to this `Node`
940  pub fn add_text_child(
941    &mut self,
942    ns: Option<Namespace>,
943    name: &str,
944    content: &str,
945  ) -> Result<Node, Box<dyn Error + Send + Sync>> {
946    let c_name = CString::new(name).unwrap();
947    let c_content = CString::new(content).unwrap();
948    let ns_ptr = match ns {
949      None => ptr::null_mut(),
950      Some(mut ns) => ns.ns_ptr_mut(),
951    };
952    unsafe {
953      let new_ptr = xmlNewTextChild(
954        self.node_ptr_mut()?,
955        ns_ptr,
956        c_name.as_bytes().as_ptr(),
957        c_content.as_bytes().as_ptr(),
958      );
959      Ok(Node::wrap(new_ptr, &self.get_docref().upgrade().unwrap()))
960    }
961  }
962
963  /// Append text to this `Node`
964  pub fn append_text(&mut self, content: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
965    let c_len = content.len() as i32;
966    if c_len > 0 {
967      let c_content = CString::new(content).unwrap();
968      unsafe {
969        xmlNodeAddContentLen(self.node_ptr_mut()?, c_content.as_bytes().as_ptr(), c_len);
970      }
971    }
972    Ok(())
973  }
974
975  /// Unbinds the Node from its siblings and Parent, but not from the Document it belongs to.
976  ///   If the node is not inserted into the DOM afterwards, it will be lost after the program terminates.
977  ///   From a low level view, the unbound node is stripped
978  ///   from the context it is and inserted into a (hidden) document-fragment.
979  pub fn unlink_node(&mut self) {
980    let node_type = self.get_type();
981    if node_type != Some(NodeType::DocumentNode)
982      && node_type != Some(NodeType::DocumentFragNode)
983      && !self.is_unlinked()
984    {
985      // only unlink nodes that are currently marked as linked
986      self.set_unlinked();
987      unsafe {
988        xmlUnlinkNode(self.node_ptr());
989      }
990    }
991  }
992  /// Alias for `unlink_node`
993  pub fn unlink(&mut self) {
994    self.unlink_node()
995  }
996  /// Alias for `unlink_node`
997  pub fn unbind_node(&mut self) {
998    self.unlink_node()
999  }
1000  /// Alias for `unlink_node`
1001  pub fn unbind(&mut self) {
1002    self.unlink_node()
1003  }
1004
1005  /// Checks if node is marked as unlinked
1006  pub fn is_unlinked(&self) -> bool {
1007    self.0.borrow().unlinked
1008  }
1009
1010  fn ptr_as_option(&self, node_ptr: xmlNodePtr) -> Option<Node> {
1011    if node_ptr.is_null() {
1012      None
1013    } else {
1014      let doc_ref = self.get_docref().upgrade().unwrap();
1015      let new_node = Node::wrap(node_ptr, &doc_ref);
1016      Some(new_node)
1017    }
1018  }
1019
1020  /// internal helper to ensure the node is marked as linked/imported/adopted in the main document tree
1021  pub(crate) fn set_linked(&self) {
1022    self.0.borrow_mut().unlinked = false;
1023  }
1024
1025  /// internal helper to ensure the node is marked as unlinked/removed from the main document tree
1026  pub(crate) fn set_unlinked(&self) {
1027    self.0.borrow_mut().unlinked = true;
1028    self
1029      .get_docref()
1030      .upgrade()
1031      .unwrap()
1032      .borrow_mut()
1033      .forget_node(self.node_ptr());
1034  }
1035
1036  /// find nodes via xpath, at a specified node or the document root
1037  pub fn findnodes(&self, xpath: &str) -> Result<Vec<Node>, ()> {
1038    let mut context = Context::from_node(self)?;
1039    context.findnodes(xpath, Some(self))
1040  }
1041
1042  /// find String values via xpath, at a specified node or the document root
1043  pub fn findvalues(&self, xpath: &str) -> Result<Vec<String>, ()> {
1044    let mut context = Context::from_node(self)?;
1045    context.findvalues(xpath, Some(self))
1046  }
1047
1048  /// replace a `self`'s `old` child node with a `new` node in the same position
1049  /// borrowed from Perl's XML::LibXML
1050  pub fn replace_child_node(
1051    &mut self,
1052    mut new: Node,
1053    mut old: Node,
1054  ) -> Result<Node, Box<dyn Error + Send + Sync>> {
1055    // if newNode == oldNode or self == newNode then do nothing, just return nNode.
1056    if new == old || self == &new {
1057      // nothing to do here, already in place
1058      Ok(old)
1059    } else if self.get_type() == Some(NodeType::ElementNode) {
1060      if let Some(old_parent) = old.get_parent() {
1061        if &old_parent == self {
1062          // unlink new to be available for insertion
1063          new.unlink();
1064          // mid-child case
1065          old.add_next_sibling(&mut new)?;
1066          old.unlink();
1067          Ok(old)
1068        } else {
1069          Err(From::from(format!(
1070            "Old node was not a child of {:?} parent. Registered parent is {:?} instead.",
1071            self.get_name(),
1072            old_parent.get_name()
1073          )))
1074        }
1075      } else {
1076        Err(From::from(format!(
1077          "Old node was not a child of {:?} parent. No registered parent exists.",
1078          self.get_name()
1079        )))
1080      }
1081    } else {
1082      Err(From::from(
1083        "Can only call replace_child_node an a NodeType::Element type parent.",
1084      ))
1085    }
1086  }
1087}