• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Contains XML qualified names manipulation types and functions.
2 //!
3 
4 use std::fmt;
5 use std::str::FromStr;
6 
7 use 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)]
48 pub 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 
59 impl<'a> From<&'a str> for Name<'a> {
from(s: &'a str) -> 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 
70 impl<'a> From<(&'a str, &'a str)> for Name<'a> {
from((prefix, name): (&'a str, &'a str)) -> Name<'a>71     fn from((prefix, name): (&'a str, &'a str)) -> Name<'a> {
72         Name::prefixed(name, prefix)
73     }
74 }
75 
76 impl<'a> fmt::Display for Name<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result77     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 
90 impl<'a> Name<'a> {
91     /// Returns an owned variant of the qualified name.
to_owned(&self) -> OwnedName92     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]
local(local_name: &str) -> Name102     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]
prefixed(local_name: &'a str, prefix: &'a str) -> Name<'a>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]
qualified(local_name: &'a str, namespace: &'a str, prefix: Option<&'a str>) -> Name<'a>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.
to_repr(&self) -> String135     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]
repr_display(&self) -> ReprDisplay145     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]
prefix_repr(&self) -> &str151     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.
158 pub struct ReprDisplay<'a, 'b:'a>(&'a Name<'b>);
159 
160 impl<'a, 'b:'a> fmt::Display for ReprDisplay<'a, 'b> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result161     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)]
173 pub 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 
184 impl fmt::Display for OwnedName {
185     #[inline]
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result186     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187         fmt::Display::fmt(&self.borrow(), f)
188     }
189 }
190 
191 impl OwnedName {
192     /// Constructs a borrowed `Name` based on this owned name.
borrow(&self) -> Name193     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]
local<S>(local_name: S) -> OwnedName where S: Into<String>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]
qualified<S1, S2, S3>(local_name: S1, namespace: S2, prefix: Option<S3>) -> OwnedName where S1: Into<String>, S2: Into<String>, S3: Into<String>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]
prefix_ref(&self) -> Option<&str>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]
namespace_ref(&self) -> Option<&str>234     pub fn namespace_ref(&self) -> Option<&str> {
235         self.namespace.as_ref().map(|s| &**s)
236     }
237 }
238 
239 impl<'a> From<Name<'a>> for OwnedName {
240     #[inline]
from(n: Name<'a>) -> OwnedName241     fn from(n: Name<'a>) -> OwnedName {
242         n.to_owned()
243     }
244 }
245 
246 impl 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.
from_str(s: &str) -> Result<OwnedName, ()>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)]
278 mod tests {
279     use super::OwnedName;
280 
281     #[test]
test_owned_name_from_str()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 }
302