sxd_document/lib.rs
1//!
2//! ```
3//! use sxd_document::Package;
4//! let package = Package::new();
5//! let doc = package.as_document();
6//!
7//! let hello = doc.create_element("hello");
8//! hello.set_attribute_value("planet", "Earth");
9//! let comment = doc.create_comment("What about other planets?");
10//! let text = doc.create_text("Greetings, Earthlings!");
11//!
12//! hello.append_child(comment);
13//! hello.append_child(text);
14//! doc.root().append_child(hello);
15//! ```
16//!
17//! ### Memory and ownership
18//!
19//! The `Package` struct is responsible for owning every node in the
20//! document. Strings are interned, allowing repeated text to consume
21//! less memory. This is very useful for documents containing lots of
22//! the same attributes and tag names.
23//!
24//! The flip side of this decision is that allocated nodes and strings
25//! are not deallocated until the entire `Package` is dropped. This is
26//! a reasonable decision for two common cases: building up an XML
27//! document and reading an XML document. You may wish to perform
28//! large modifications to your data *before* creating a document.
29//!
30//! ### Namespaces, QNames, and Prefixes
31//!
32//! The names of elements and attributes may use namespaces. XML
33//! namespacing uses URIs to uniquely distinguish items with the same
34//! local name. A qualified name (`QName`) combines this optional URI
35//! with the local name.
36//!
37//! When an XML document is represented as text, namespaces are given
38//! a shorthand reference known as a prefix. Prefix names are
39//! non-authoritative, and only the URI can be used to namespace a
40//! name.
41//!
42//! Elements and attributes may specify a *preferred prefix*, which is
43//! an indication of what the user would like to be used as a
44//! prefix. There are times where the preferred prefix would cause a
45//! conflict, and so an autogenerated prefix will be used instead.
46//!
47//! ### Design decisions
48//!
49//! Try to leverage the type system as much as possible.
50
51#![cfg_attr(feature = "unstable", feature(pattern))]
52#![cfg_attr(feature = "unstable", feature(test))]
53
54extern crate typed_arena;
55
56#[macro_use]
57extern crate peresil;
58
59use std::fmt;
60
61mod str_ext;
62mod lazy_hash_map;
63mod string_pool;
64mod raw;
65mod str;
66
67#[doc(hidden)]
68pub mod thindom;
69pub mod dom;
70pub mod parser;
71pub mod writer;
72
73pub use str::XmlChar;
74
75static XML_NS_PREFIX: &'static str = "xml";
76static XML_NS_URI:    &'static str = "http://www.w3.org/XML/1998/namespace";
77
78/// A prefixed name. This represents what is found in the string form
79/// of an XML document, and does not apply any namespace mapping.
80#[derive(Debug,Copy,Clone,PartialEq,Eq,PartialOrd,Ord)]
81pub struct PrefixedName<'a> {
82    prefix: Option<&'a str>,
83    local_part: &'a str,
84}
85
86impl<'a> PrefixedName<'a> {
87    /// Create a `PrefixedName` without a prefix
88    pub fn new(local_part: &str) -> PrefixedName {
89        PrefixedName::with_prefix(None, local_part)
90    }
91
92    /// Create a `PrefixedName` without an optional prefix
93    pub fn with_prefix(prefix: Option<&'a str>, local_part: &'a str) -> PrefixedName<'a> {
94        PrefixedName {
95            prefix: prefix,
96            local_part: local_part,
97        }
98    }
99
100    pub fn prefix(&self) -> Option<&str> { self.prefix }
101    pub fn local_part(&self) -> &str { self.local_part }
102}
103
104/// A namespace-qualified name. This represents the name of an element
105/// or attribute *after* the prefix has been mapped to a specific
106/// namespace.
107#[derive(Debug,Copy,Clone,PartialEq)]
108pub struct QName<'s> {
109    namespace_uri: Option<&'s str>,
110    local_part: &'s str,
111}
112
113impl<'s> QName<'s> {
114    /// Create a `QName` without a namespace
115    pub fn new(local_part: &'s str) -> QName<'s> {
116        QName::with_namespace_uri(None, local_part)
117    }
118
119    /// Create a `QName` with an optional namespace
120    pub fn with_namespace_uri(namespace_uri: Option<&'s str>, local_part: &'s str) -> QName<'s> {
121        QName {
122            namespace_uri: namespace_uri,
123            local_part: local_part,
124        }
125    }
126
127    pub fn namespace_uri(&self) -> Option<&'s str> { self.namespace_uri }
128    pub fn local_part(&self) -> &'s str { self.local_part }
129}
130
131impl<'s> From<(&'s str, &'s str)> for QName<'s> {
132    fn from(v: (&'s str, &'s str)) -> QName<'s> {
133        QName { namespace_uri: Some(v.0), local_part: v.1 }
134    }
135}
136
137impl<'s> From<&'s str> for QName<'s> {
138    fn from(v: &'s str) -> QName<'s> {
139        QName { namespace_uri: None, local_part: v }
140    }
141}
142
143/// The main entrypoint to an XML document
144///
145/// This is an opaque structure that stores the internal details of
146/// the XML document. Modify the document via `as_document`.
147pub struct Package {
148    storage: raw::Storage,
149    connections: raw::Connections,
150}
151
152impl Package {
153    pub fn new() -> Package {
154        let s = raw::Storage::new();
155        let root = s.create_root();
156        Package {
157            storage: s,
158            connections: raw::Connections::new(root),
159        }
160    }
161
162    pub fn as_document(&self) -> dom::Document {
163        dom::Document::new(&self.storage, &self.connections)
164    }
165
166    #[doc(hidden)]
167    pub fn as_thin_document(&self) -> (thindom::Storage, thindom::Connections) {
168        let s = thindom::Storage::new(&self.storage);
169        let c = thindom::Connections::new(&self.connections);
170        (s, c)
171    }
172}
173
174impl PartialEq for Package {
175    fn eq(&self, other: &Package) -> bool {
176        self as *const Package == other as *const Package
177    }
178}
179
180impl fmt::Debug for Package {
181    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
182        write!(f, "Package")
183    }
184}