robinson/
lib.rs

1#![deny(
2    unsafe_code,
3    missing_debug_implementations,
4    missing_copy_implementations,
5    unreachable_pub
6)]
7
8mod attributes;
9mod error;
10mod namespaces;
11mod nodes;
12mod parser;
13#[cfg(feature = "serde")]
14pub mod serde;
15mod strings;
16mod tokenizer;
17
18use std::fmt;
19use std::num::NonZeroUsize;
20
21use attributes::AttributeData;
22use namespaces::{Namespace, Namespaces, NamespacesBuilder};
23use nodes::{ElementData, NodeData};
24use strings::StringData;
25
26pub use attributes::{Attribute, Attributes};
27pub use error::{Error, ErrorKind};
28pub use nodes::{Children, Descendants, Node, NodeId};
29
30pub struct Document<'input> {
31    nodes: Box<[NodeData]>,
32    elements: Box<[ElementData<'input>]>,
33    texts: Box<[StringData<'input>]>,
34    attributes: Box<[AttributeData<'input>]>,
35    namespaces: Namespaces<'input>,
36}
37
38const fn _is_send_and_sync<T>()
39where
40    T: Send + Sync,
41{
42}
43
44const _DOCUMENT_IS_SEND_AND_SYNC: () = _is_send_and_sync::<Document>();
45
46impl Document<'_> {
47    pub fn len(&self) -> NonZeroUsize {
48        NonZeroUsize::new(self.nodes.len()).unwrap()
49    }
50}
51
52struct DocumentBuilder<'input> {
53    nodes: Vec<NodeData>,
54    elements: Vec<ElementData<'input>>,
55    texts: Vec<StringData<'input>>,
56    attributes: Vec<AttributeData<'input>>,
57    namespaces: NamespacesBuilder<'input>,
58}
59
60impl<'input> DocumentBuilder<'input> {
61    fn build(self) -> Document<'input> {
62        Document {
63            nodes: self.nodes.into_boxed_slice(),
64            elements: self.elements.into_boxed_slice(),
65            texts: self.texts.into_boxed_slice(),
66            attributes: self.attributes.into_boxed_slice(),
67            namespaces: self.namespaces.build(),
68        }
69    }
70}
71
72#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
73pub struct Name<'doc, 'input> {
74    pub namespace: Option<&'doc str>,
75    pub local: &'input str,
76}
77
78/// ```
79/// # use robinson::Document;
80/// let doc = Document::parse(r#"<foo xmlns="http://bar"/>"#).unwrap();
81///
82/// let root_name = doc.root_element().name().unwrap();
83///
84/// assert_eq!(root_name.namespace, Some("http://bar"));
85/// assert_eq!(root_name.local, "foo");
86///
87/// assert_eq!(root_name, "foo");
88/// ```
89impl PartialEq<&str> for Name<'_, '_> {
90    fn eq(&self, other: &&str) -> bool {
91        self.local == *other
92    }
93}
94
95#[derive(Debug, Clone, Copy)]
96struct NameData<'input> {
97    namespace: Option<Namespace>,
98    local: &'input str,
99}
100
101const _SIZE_OF_NAME_DATA: () =
102    assert!(size_of::<NameData<'static>>() == (1 + 2) * size_of::<usize>());
103
104impl<'input> NameData<'input> {
105    fn get<'doc>(self, doc: &'doc Document) -> Name<'doc, 'input> {
106        let namespace = self
107            .namespace
108            .map(|namespace| doc.namespaces.get(namespace));
109
110        Name {
111            namespace,
112            local: self.local,
113        }
114    }
115}
116
117impl fmt::Debug for Document<'_> {
118    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
119        fmt.debug_struct("Document")
120            .field("root", &self.root())
121            .finish()
122    }
123}
124
125impl fmt::Debug for Name<'_, '_> {
126    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
127        match self.namespace {
128            Some(namespace) => write!(fmt, "\"{{{}}}{}\"", namespace, self.local),
129            None => write!(fmt, "\"{}\"", self.local),
130        }
131    }
132}