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