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}