xml/name.rs
1//! Contains XML qualified names manipulation types and functions.
2//!
3
4use std::fmt;
5use std::str::FromStr;
6
7use namespace::NS_NO_PREFIX;
8
9/// Represents a qualified XML name.
10///
11/// A qualified name always consists at least of a local name. It can optionally contain
12/// a prefix; when reading an XML document, if it contains a prefix, it must also contain a
13/// namespace URI, but this is not enforced statically; see below. The name can contain a
14/// namespace without a prefix; in that case a default, empty prefix is assumed.
15///
16/// When writing XML documents, it is possible to omit the namespace URI, leaving only
17/// the prefix. In this case the writer will check that the specifed prefix is bound to some
18/// URI in the current namespace context. If both prefix and namespace URI are specified,
19/// it is checked that the current namespace context contains this exact correspondence
20/// between prefix and namespace URI.
21///
22/// # Prefixes and URIs
23///
24/// A qualified name with a prefix must always contain a proper namespace URI --- names with
25/// a prefix but without a namespace associated with that prefix are meaningless. However,
26/// it is impossible to obtain proper namespace URI by a prefix without a context, and such
27/// context is only available when parsing a document (or it can be constructed manually
28/// when writing a document). Tying a name to a context statically seems impractical. This
29/// may change in future, though.
30///
31/// # Conversions
32///
33/// `Name` implements some `From` instances for conversion from strings and tuples. For example:
34///
35/// ```rust
36/// # use xml::name::Name;
37/// let n1: Name = "p:some-name".into();
38/// let n2: Name = ("p", "some-name").into();
39///
40/// assert_eq!(n1, n2);
41/// assert_eq!(n1.local_name, "some-name");
42/// assert_eq!(n1.prefix, Some("p"));
43/// assert!(n1.namespace.is_none());
44/// ```
45///
46/// This is added to support easy specification of XML elements when writing XML documents.
47#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
48pub struct Name<'a> {
49 /// A local name, e.g. `string` in `xsi:string`.
50 pub local_name: &'a str,
51
52 /// A namespace URI, e.g. `http://www.w3.org/2000/xmlns/`.
53 pub namespace: Option<&'a str>,
54
55 /// A name prefix, e.g. `xsi` in `xsi:string`.
56 pub prefix: Option<&'a str>
57}
58
59impl<'a> From<&'a str> for Name<'a> {
60 fn from(s: &'a str) -> Name<'a> {
61 let mut parts = s.splitn(2, ":").fuse();
62 match (parts.next(), parts.next()) {
63 (Some(name), None) => Name::local(name),
64 (Some(prefix), Some(name)) => Name::prefixed(name, prefix),
65 _ => unreachable!()
66 }
67 }
68}
69
70impl<'a> From<(&'a str, &'a str)> for Name<'a> {
71 fn from((prefix, name): (&'a str, &'a str)) -> Name<'a> {
72 Name::prefixed(name, prefix)
73 }
74}
75
76impl<'a> fmt::Display for Name<'a> {
77 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78 if let Some(namespace) = self.namespace {
79 write!(f, "{{{}}}", namespace)?;
80 }
81
82 if let Some(prefix) = self.prefix {
83 write!(f, "{}:", prefix)?;
84 }
85
86 write!(f, "{}", self.local_name)
87 }
88}
89
90impl<'a> Name<'a> {
91 /// Returns an owned variant of the qualified name.
92 pub fn to_owned(&self) -> OwnedName {
93 OwnedName {
94 local_name: self.local_name.into(),
95 namespace: self.namespace.map(|s| s.into()),
96 prefix: self.prefix.map(|s| s.into())
97 }
98 }
99
100 /// Returns a new `Name` instance representing plain local name.
101 #[inline]
102 pub fn local(local_name: &str) -> Name {
103 Name {
104 local_name,
105 prefix: None,
106 namespace: None
107 }
108 }
109
110 /// Returns a new `Name` instance with the given local name and prefix.
111 #[inline]
112 pub fn prefixed(local_name: &'a str, prefix: &'a str) -> Name<'a> {
113 Name {
114 local_name,
115 namespace: None,
116 prefix: Some(prefix)
117 }
118 }
119
120 /// Returns a new `Name` instance representing a qualified name with or without a prefix and
121 /// with a namespace URI.
122 #[inline]
123 pub fn qualified(local_name: &'a str, namespace: &'a str, prefix: Option<&'a str>) -> Name<'a> {
124 Name {
125 local_name,
126 namespace: Some(namespace),
127 prefix,
128 }
129 }
130
131 /// Returns a correct XML representation of this local name and prefix.
132 ///
133 /// This method is different from the autoimplemented `to_string()` because it does not
134 /// include namespace URI in the result.
135 pub fn to_repr(&self) -> String {
136 self.repr_display().to_string()
137 }
138
139 /// Returns a structure which can be displayed with `std::fmt` machinery to obtain this
140 /// local name and prefix.
141 ///
142 /// This method is needed for efficiency purposes in order not to create unnecessary
143 /// allocations.
144 #[inline]
145 pub fn repr_display(&self) -> ReprDisplay {
146 ReprDisplay(self)
147 }
148
149 /// Returns either a prefix of this name or `namespace::NS_NO_PREFIX` constant.
150 #[inline]
151 pub fn prefix_repr(&self) -> &str {
152 self.prefix.unwrap_or(NS_NO_PREFIX)
153 }
154}
155
156/// A wrapper around `Name` whose `Display` implementation prints the wrapped name as it is
157/// displayed in an XML document.
158pub struct ReprDisplay<'a, 'b:'a>(&'a Name<'b>);
159
160impl<'a, 'b:'a> fmt::Display for ReprDisplay<'a, 'b> {
161 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
162 match self.0.prefix {
163 Some(prefix) => write!(f, "{}:{}", prefix, self.0.local_name),
164 None => write!(f, "{}", self.0.local_name)
165 }
166 }
167}
168
169/// An owned variant of `Name`.
170///
171/// Everything about `Name` applies to this structure as well.
172#[derive(Clone, PartialEq, Eq, Hash, Debug)]
173pub struct OwnedName {
174 /// A local name, e.g. `string` in `xsi:string`.
175 pub local_name: String,
176
177 /// A namespace URI, e.g. `http://www.w3.org/2000/xmlns/`.
178 pub namespace: Option<String>,
179
180 /// A name prefix, e.g. `xsi` in `xsi:string`.
181 pub prefix: Option<String>,
182}
183
184impl fmt::Display for OwnedName {
185 #[inline]
186 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187 fmt::Display::fmt(&self.borrow(), f)
188 }
189}
190
191impl OwnedName {
192 /// Constructs a borrowed `Name` based on this owned name.
193 pub fn borrow(&self) -> Name {
194 Name {
195 local_name: &*self.local_name,
196 namespace: self.namespace.as_ref().map(|s| &**s),
197 prefix: self.prefix.as_ref().map(|s| &**s),
198 }
199 }
200
201 /// Returns a new `OwnedName` instance representing a plain local name.
202 #[inline]
203 pub fn local<S>(local_name: S) -> OwnedName where S: Into<String> {
204 OwnedName {
205 local_name: local_name.into(),
206 namespace: None,
207 prefix: None,
208 }
209 }
210
211 /// Returns a new `OwnedName` instance representing a qualified name with or without
212 /// a prefix and with a namespace URI.
213 #[inline]
214 pub fn qualified<S1, S2, S3>(local_name: S1, namespace: S2, prefix: Option<S3>) -> OwnedName
215 where S1: Into<String>, S2: Into<String>, S3: Into<String>
216 {
217 OwnedName {
218 local_name: local_name.into(),
219 namespace: Some(namespace.into()),
220 prefix: prefix.map(|v| v.into())
221 }
222 }
223
224 /// Returns an optional prefix by reference, equivalent to `self.borrow().prefix`
225 /// but avoids extra work.
226 #[inline]
227 pub fn prefix_ref(&self) -> Option<&str> {
228 self.prefix.as_ref().map(|s| &**s)
229 }
230
231 /// Returns an optional namespace by reference, equivalen to `self.borrow().namespace`
232 /// but avoids extra work.
233 #[inline]
234 pub fn namespace_ref(&self) -> Option<&str> {
235 self.namespace.as_ref().map(|s| &**s)
236 }
237}
238
239impl<'a> From<Name<'a>> for OwnedName {
240 #[inline]
241 fn from(n: Name<'a>) -> OwnedName {
242 n.to_owned()
243 }
244}
245
246impl FromStr for OwnedName {
247 type Err = ();
248
249 /// Parses the given string slice into a qualified name.
250 ///
251 /// This function, when finishes sucessfully, always return a qualified
252 /// name without a namespace (`name.namespace == None`). It should be filled later
253 /// using proper `NamespaceStack`.
254 ///
255 /// It is supposed that all characters in the argument string are correct
256 /// as defined by the XML specification. No additional checks except a check
257 /// for emptiness are done.
258 fn from_str(s: &str) -> Result<OwnedName, ()> {
259 let mut it = s.split(':');
260
261 let r = match (it.next(), it.next(), it.next()) {
262 (Some(prefix), Some(local_name), None) if !prefix.is_empty() &&
263 !local_name.is_empty() =>
264 Some((local_name.into(), Some(prefix.into()))),
265 (Some(local_name), None, None) if !local_name.is_empty() =>
266 Some((local_name.into(), None)),
267 (_, _, _) => None
268 };
269 r.map(|(local_name, prefix)| OwnedName {
270 local_name,
271 namespace: None,
272 prefix
273 }).ok_or(())
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::OwnedName;
280
281 #[test]
282 fn test_owned_name_from_str() {
283 assert_eq!("prefix:name".parse(), Ok(OwnedName {
284 local_name: "name".into(),
285 namespace: None,
286 prefix: Some("prefix".into())
287 }));
288
289 assert_eq!("name".parse(), Ok(OwnedName {
290 local_name: "name".into(),
291 namespace: None,
292 prefix: None
293 }));
294
295 assert_eq!("".parse(), Err::<OwnedName, ()>(()));
296 assert_eq!(":".parse(), Err::<OwnedName, ()>(()));
297 assert_eq!(":a".parse(), Err::<OwnedName, ()>(()));
298 assert_eq!("a:".parse(), Err::<OwnedName, ()>(()));
299 assert_eq!("a:b:c".parse(), Err::<OwnedName, ()>(()));
300 }
301}