1//! Contains XML qualified names manipulation types and functions.
2//!
3
4use std::fmt;
5use std::str::FromStr;
6
7use 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)]
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: impl Iterator = s.splitn(n:2, pat:':').fuse();
62 match (parts.next(), parts.next()) {
63 (Some(name: &str), None) => Name::local(local_name:name),
64 (Some(prefix: &str), Some(name: &str)) => Name::prefixed(local_name:name, prefix),
65 _ => unreachable!(),
66 }
67 }
68}
69
70impl<'a> From<(&'a str, &'a str)> for Name<'a> {
71 fn from((prefix: &str, name: &str): (&'a str, &'a str)) -> Name<'a> {
72 Name::prefixed(local_name: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: &str) = self.namespace {
79 write!(f, "{{{namespace}}}")?;
80 }
81
82 if let Some(prefix: &str) = self.prefix {
83 write!(f, "{prefix}:")?;
84 }
85
86 f.write_str(self.local_name)
87 }
88}
89
90impl<'a> Name<'a> {
91 /// Returns an owned variant of the qualified name.
92 #[must_use]
93 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]
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]
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]
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]
140 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]
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]
158 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.
165pub struct ReprDisplay<'a, 'b>(&'a Name<'b>);
166
167impl<'a, 'b: 'a> fmt::Display for ReprDisplay<'a, 'b> {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 match self.0.prefix {
170 Some(prefix: &str) => 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)]
180pub 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
191impl fmt::Display for OwnedName {
192 #[inline]
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 fmt::Display::fmt(&self.borrow(), f)
195 }
196}
197
198impl OwnedName {
199 /// Constructs a borrowed `Name` based on this owned name.
200 #[must_use]
201 #[inline]
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]
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]
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]
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]
245 pub fn namespace_ref(&self) -> Option<&str> {
246 self.namespace.as_deref()
247 }
248}
249
250impl<'a> From<Name<'a>> for OwnedName {
251 #[inline]
252 fn from(n: Name<'a>) -> OwnedName {
253 n.to_owned()
254 }
255}
256
257impl 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.
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)]
289mod tests {
290 use super::OwnedName;
291
292 #[test]
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