1 //! Contains XML qualified names manipulation types and functions.
2 //!
3 
4 use std::fmt;
5 use std::str::FromStr;
6 
7 use crate::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         f.write_str(self.local_name)
87     }
88 }
89 
90 impl<'a> Name<'a> {
91     /// Returns an owned variant of the qualified name.
92     #[must_use]
to_owned(&self) -> OwnedName93     pub fn to_owned(&self) -> OwnedName {
94         OwnedName {
95             local_name: self.local_name.into(),
96             namespace: self.namespace.map(std::convert::Into::into),
97             prefix: self.prefix.map(std::convert::Into::into),
98         }
99     }
100 
101     /// Returns a new `Name` instance representing plain local name.
102     #[inline]
103     #[must_use]
local(local_name: &str) -> Name<'_>104     pub fn local(local_name: &str) -> Name<'_> {
105         Name {
106             local_name,
107             prefix: None,
108             namespace: None,
109         }
110     }
111 
112     /// Returns a new `Name` instance with the given local name and prefix.
113     #[inline]
114     #[must_use]
prefixed(local_name: &'a str, prefix: &'a str) -> Name<'a>115     pub fn prefixed(local_name: &'a str, prefix: &'a str) -> Name<'a> {
116         Name {
117             local_name,
118             namespace: None,
119             prefix: Some(prefix),
120         }
121     }
122 
123     /// Returns a new `Name` instance representing a qualified name with or without a prefix and
124     /// with a namespace URI.
125     #[inline]
126     #[must_use]
qualified(local_name: &'a str, namespace: &'a str, prefix: Option<&'a str>) -> Name<'a>127     pub fn qualified(local_name: &'a str, namespace: &'a str, prefix: Option<&'a str>) -> Name<'a> {
128         Name {
129             local_name,
130             namespace: Some(namespace),
131             prefix,
132         }
133     }
134 
135     /// Returns a correct XML representation of this local name and prefix.
136     ///
137     /// This method is different from the autoimplemented `to_string()` because it does not
138     /// include namespace URI in the result.
139     #[must_use]
to_repr(&self) -> String140     pub fn to_repr(&self) -> String {
141         self.repr_display().to_string()
142     }
143 
144     /// Returns a structure which can be displayed with `std::fmt` machinery to obtain this
145     /// local name and prefix.
146     ///
147     /// This method is needed for efficiency purposes in order not to create unnecessary
148     /// allocations.
149     #[inline]
150     #[must_use]
repr_display(&self) -> ReprDisplay<'_, '_>151     pub fn repr_display(&self) -> ReprDisplay<'_, '_> {
152         ReprDisplay(self)
153     }
154 
155     /// Returns either a prefix of this name or `namespace::NS_NO_PREFIX` constant.
156     #[inline]
157     #[must_use]
prefix_repr(&self) -> &str158     pub fn prefix_repr(&self) -> &str {
159         self.prefix.unwrap_or(NS_NO_PREFIX)
160     }
161 }
162 
163 /// A wrapper around `Name` whose `Display` implementation prints the wrapped name as it is
164 /// displayed in an XML document.
165 pub struct ReprDisplay<'a, 'b>(&'a Name<'b>);
166 
167 impl<'a, 'b: 'a> fmt::Display for ReprDisplay<'a, 'b> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result168     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169         match self.0.prefix {
170             Some(prefix) => write!(f, "{}:{}", prefix, self.0.local_name),
171             None => self.0.local_name.fmt(f),
172         }
173     }
174 }
175 
176 /// An owned variant of `Name`.
177 ///
178 /// Everything about `Name` applies to this structure as well.
179 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
180 pub struct OwnedName {
181     /// A local name, e.g. `string` in `xsi:string`.
182     pub local_name: String,
183 
184     /// A namespace URI, e.g. `http://www.w3.org/2000/xmlns/`.
185     pub namespace: Option<String>,
186 
187     /// A name prefix, e.g. `xsi` in `xsi:string`.
188     pub prefix: Option<String>,
189 }
190 
191 impl fmt::Display for OwnedName {
192     #[inline]
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result193     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194         fmt::Display::fmt(&self.borrow(), f)
195     }
196 }
197 
198 impl OwnedName {
199     /// Constructs a borrowed `Name` based on this owned name.
200     #[must_use]
201     #[inline]
borrow(&self) -> Name<'_>202     pub fn borrow(&self) -> Name<'_> {
203         Name {
204             local_name: &self.local_name,
205             namespace: self.namespace.as_deref(),
206             prefix: self.prefix.as_deref(),
207         }
208     }
209 
210     /// Returns a new `OwnedName` instance representing a plain local name.
211     #[inline]
local<S>(local_name: S) -> OwnedName where S: Into<String>212     pub fn local<S>(local_name: S) -> OwnedName where S: Into<String> {
213         OwnedName {
214             local_name: local_name.into(),
215             namespace: None,
216             prefix: None,
217         }
218     }
219 
220     /// Returns a new `OwnedName` instance representing a qualified name with or without
221     /// a prefix and with a namespace URI.
222     #[inline]
qualified<S1, S2, S3>(local_name: S1, namespace: S2, prefix: Option<S3>) -> OwnedName where S1: Into<String>, S2: Into<String>, S3: Into<String>223     pub fn qualified<S1, S2, S3>(local_name: S1, namespace: S2, prefix: Option<S3>) -> OwnedName
224         where S1: Into<String>, S2: Into<String>, S3: Into<String>
225     {
226         OwnedName {
227             local_name: local_name.into(),
228             namespace: Some(namespace.into()),
229             prefix: prefix.map(std::convert::Into::into),
230         }
231     }
232 
233     /// Returns an optional prefix by reference, equivalent to `self.borrow().prefix`
234     /// but avoids extra work.
235     #[inline]
236     #[must_use]
prefix_ref(&self) -> Option<&str>237     pub fn prefix_ref(&self) -> Option<&str> {
238         self.prefix.as_deref()
239     }
240 
241     /// Returns an optional namespace by reference, equivalen to `self.borrow().namespace`
242     /// but avoids extra work.
243     #[inline]
244     #[must_use]
namespace_ref(&self) -> Option<&str>245     pub fn namespace_ref(&self) -> Option<&str> {
246         self.namespace.as_deref()
247     }
248 }
249 
250 impl<'a> From<Name<'a>> for OwnedName {
251     #[inline]
from(n: Name<'a>) -> OwnedName252     fn from(n: Name<'a>) -> OwnedName {
253         n.to_owned()
254     }
255 }
256 
257 impl FromStr for OwnedName {
258     type Err = ();
259 
260     /// Parses the given string slice into a qualified name.
261     ///
262     /// This function, when finishes sucessfully, always return a qualified
263     /// name without a namespace (`name.namespace == None`). It should be filled later
264     /// using proper `NamespaceStack`.
265     ///
266     /// It is supposed that all characters in the argument string are correct
267     /// as defined by the XML specification. No additional checks except a check
268     /// for emptiness are done.
from_str(s: &str) -> Result<OwnedName, ()>269     fn from_str(s: &str) -> Result<OwnedName, ()> {
270         let mut it = s.split(':');
271 
272         let r = match (it.next(), it.next(), it.next()) {
273             (Some(prefix), Some(local_name), None) if !prefix.is_empty() &&
274                                                       !local_name.is_empty() =>
275                 Some((local_name.into(), Some(prefix.into()))),
276             (Some(local_name), None, None) if !local_name.is_empty() =>
277                 Some((local_name.into(), None)),
278             (_, _, _) => None
279         };
280         r.map(|(local_name, prefix)| OwnedName {
281             local_name,
282             namespace: None,
283             prefix
284         }).ok_or(())
285     }
286 }
287 
288 #[cfg(test)]
289 mod tests {
290     use super::OwnedName;
291 
292     #[test]
test_owned_name_from_str()293     fn test_owned_name_from_str() {
294         assert_eq!("prefix:name".parse(), Ok(OwnedName {
295             local_name: "name".into(),
296             namespace: None,
297             prefix: Some("prefix".into())
298         }));
299 
300         assert_eq!("name".parse(), Ok(OwnedName {
301             local_name: "name".into(),
302             namespace: None,
303             prefix: None
304         }));
305 
306         assert_eq!("".parse(), Err::<OwnedName, ()>(()));
307         assert_eq!(":".parse(), Err::<OwnedName, ()>(()));
308         assert_eq!(":a".parse(), Err::<OwnedName, ()>(()));
309         assert_eq!("a:".parse(), Err::<OwnedName, ()>(()));
310         assert_eq!("a:b:c".parse(), Err::<OwnedName, ()>(()));
311     }
312 }
313