xml_string/names/eqname.rs
1//! [`EQName`]: `QName` OR `URIQualifiedName`.
2//!
3//! [`EQName`]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#prod-xpath31-EQName
4
5use core::cmp;
6use core::convert::TryFrom;
7use core::fmt;
8use core::hash;
9use core::num::NonZeroUsize;
10
11use crate::names::error::{NameError, TargetNameType};
12use crate::names::{Ncname, ParsedQname, ParsedUriQualifiedName, Qname, UriQualifiedName};
13
14/// A type for data which is specific to each variant of [`EQName`][spec-eqname]:
15/// [`QName`][spec-qname] or [`URIQualifiedName`][spec-uqn].
16///
17/// [spec-eqname]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#doc-xpath31-EQName
18/// [spec-qname]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#doc-xpath31-QName
19/// [spec-uqn]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#doc-xpath31-URIQualifiedName
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum EqnameVariantData<Q, U> {
22 /// Data specific to `QName`.
23 Q(Q),
24 /// Data specific to `URIQualifiedName`.
25 UriQualified(U),
26}
27
28impl<Q, U> EqnameVariantData<Q, U> {
29 /// Returns the variant data with reference types.
30 fn as_ref(&self) -> EqnameVariantData<&Q, &U> {
31 match self {
32 Self::Q(v) => EqnameVariantData::Q(v),
33 Self::UriQualified(v) => EqnameVariantData::UriQualified(v),
34 }
35 }
36}
37
38/// A type to represent namespace of an [`EQName`][spec-eqname].
39///
40/// If the `EQName` is a [`QName`][spec-qname], then returns `None` or `Prefix(prefix)`.
41/// If the name is a [`URIQualifiedName`][spec-uqn], then returns `Uri(uri)`.
42///
43/// [spec-eqname]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#doc-xpath31-EQName
44/// [spec-qname]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#doc-xpath31-QName
45/// [spec-uqn]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#doc-xpath31-URIQualifiedName
46// Practically, this can be represented by `Option<EqnameVariantData<&Ncname, &str>>`,
47// but more specific type is preferred. Putting namespace URI in `&str` and
48// prefix in `&Ncname` to the same type without "URI" and "prefix" annotation
49// would be very confusing.
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum EqnameNamespace<'a> {
52 /// No prefix or namespace.
53 None,
54 /// Namespace prefix.
55 Prefix(&'a Ncname),
56 /// Namespace URI.
57 Uri(&'a str),
58}
59
60/// String slice for [`EQName`][spec-eqname].
61///
62/// [`EQName`][spec-eqname] is union of [`QName`][spec-qname] and
63/// [`URIQualifiedName`][spec-uqn].
64/// See the documentation for [`Qname`] type and [`UriQualifiedName`] type.
65///
66/// [spec-eqname]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#doc-xpath31-EQName
67/// [spec-qname]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#doc-xpath31-QName
68/// [spec-uqn]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#doc-xpath31-URIQualifiedName
69#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
70#[repr(transparent)]
71pub struct Eqname(str);
72
73#[allow(clippy::len_without_is_empty)]
74impl Eqname {
75 /// Creates a new `&UriQualifiedName`.
76 ///
77 /// # Failures
78 ///
79 /// Fails if the given string is not a valid [`EQName`].
80 ///
81 /// # Examples
82 ///
83 /// ```
84 /// # use xml_string::names::Eqname;
85 /// let prefixed_q = Eqname::from_str("foo:bar")?;
86 /// assert_eq!(prefixed_q, "foo:bar");
87 ///
88 /// let nc = Eqname::from_str("ncname")?;
89 /// assert_eq!(nc, "ncname");
90 ///
91 /// let uri_qualified = Eqname::from_str("Q{http://example.com/}name")?;
92 /// assert_eq!(uri_qualified, "Q{http://example.com/}name");
93 ///
94 /// assert_eq!(
95 /// Eqname::from_str("Q{}name")?,
96 /// "Q{}name",
97 /// "Empty URI is OK"
98 /// );
99 /// assert_eq!(
100 /// Eqname::from_str("Q{foo}bar")?,
101 /// "Q{foo}bar",
102 /// "URI is not validated"
103 /// );
104 /// # Ok::<_, xml_string::names::NameError>(())
105 /// ```
106 ///
107 /// [`EQName`]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#doc-xpath31-EQName
108 // `FromStr` can be implemented only for types with static lifetime.
109 #[allow(clippy::should_implement_trait)]
110 pub fn from_str(s: &str) -> Result<&Self, NameError> {
111 <&Self>::try_from(s)
112 }
113
114 /// Creates a new `&Eqname` without validation.
115 ///
116 /// # Safety
117 ///
118 /// The given string should be a valid [`EQName`].
119 ///
120 /// # Examples
121 ///
122 /// ```
123 /// # use xml_string::names::Eqname;
124 /// let q = unsafe {
125 /// Eqname::new_unchecked("foo:bar")
126 /// };
127 /// assert_eq!(q, "foo:bar");
128 ///
129 /// let uri_qualified = unsafe {
130 /// Eqname::new_unchecked("Q{foo}bar")
131 /// };
132 /// assert_eq!(uri_qualified, "Q{foo}bar");
133 /// ```
134 ///
135 /// [`EQName`]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#doc-xpath31-EQName
136 #[inline]
137 #[must_use]
138 pub unsafe fn new_unchecked(s: &str) -> &Self {
139 &*(s as *const str as *const Self)
140 }
141
142 /// Validates the given string.
143 fn validate(s: &str) -> Result<(), NameError> {
144 match Self::parse_as_possible(s) {
145 Ok(_) => Ok(()),
146 Err(e) => Err(NameError::new(
147 TargetNameType::Eqname,
148 e.map_or(0, |(_local_name_start, valid_up_to)| valid_up_to.get()),
149 )),
150 }
151 }
152
153 /// Returns whether the string should be parsed as URIQualifiedName.
154 ///
155 /// Note that this should be called only for a valid EQName.
156 /// Never use this for non-validated strings.
157 #[inline]
158 fn should_be_parsed_as_uri_qualified(s: &str) -> bool {
159 s.as_bytes()[1] == b'{'
160 }
161
162 /// Parses the given string from the beginning as possible.
163 ///
164 /// Retruns `Ok(EqnameVariantData::Q(local_name_start))` if the string is valid QName.
165 /// Retruns `Ok(EqnameVariantData::UriQualified(local_name_start))` if the string is valid
166 /// URIQualifiedName.
167 /// Returns `Err(None)` if the string is completely invalid.
168 /// Returns `Err(Some((local_name_start, valid_up_to)))` if the string is invalid
169 /// but has valid substring as the prefix.
170 fn parse_as_possible(
171 s: &str,
172 ) -> Result<EqnameVariantData<usize, NonZeroUsize>, Option<(usize, NonZeroUsize)>> {
173 // This check should not be `Self::should_be_parsed_as_uri_qualified`,
174 // because the string is not yet validated.
175 if s.starts_with("Q{") {
176 UriQualifiedName::parse_as_possible(s)
177 .map(EqnameVariantData::UriQualified)
178 .map_err(|e| {
179 Some(match e {
180 None => (0, NonZeroUsize::new(1).expect("1 is not zero")),
181 Some((local_name_start, valid_up_to)) => {
182 (local_name_start.get(), valid_up_to)
183 }
184 })
185 })
186 } else {
187 match Qname::parse_as_possible(s) {
188 Ok(colon_pos) => {
189 let local_name_start = colon_pos.map_or(0, |v| v.get() + 1);
190 Ok(EqnameVariantData::Q(local_name_start))
191 }
192 Err((colon_pos, valid_up_to)) => Err(NonZeroUsize::new(valid_up_to)
193 .map(|valid_up_to| (colon_pos.map_or(0, |v| v.get() + 1), valid_up_to))),
194 }
195 }
196 }
197
198 /// Returns the string as `&str`.
199 ///
200 /// # Examples
201 ///
202 /// ```
203 /// # use xml_string::names::Eqname;
204 /// let q = Eqname::from_str("foo:bar")?;
205 /// assert_eq!(q, "foo:bar");
206 /// assert_eq!(q.as_str(), "foo:bar");
207 ///
208 /// let uri_qualified = Eqname::from_str("Q{foo}bar")?;
209 /// assert_eq!(uri_qualified, "Q{foo}bar");
210 /// assert_eq!(uri_qualified.as_str(), "Q{foo}bar");
211 /// # Ok::<_, xml_string::names::NameError>(())
212 /// ```
213 #[inline]
214 #[must_use]
215 pub fn as_str(&self) -> &str {
216 &self.0
217 }
218
219 /// Returns the length of the string in bytes.
220 ///
221 /// # Examples
222 ///
223 /// ```
224 /// # use xml_string::names::Eqname;
225 /// let q = Eqname::from_str("foo:bar")?;
226 /// assert_eq!(q.len(), "foo:bar".len());
227 ///
228 /// let uri_qualified = Eqname::from_str("Q{foo}bar")?;
229 /// assert_eq!(uri_qualified.len(), "Q{foo}bar".len());
230 /// # Ok::<_, xml_string::names::NameError>(())
231 /// ```
232 #[inline]
233 #[must_use]
234 pub fn len(&self) -> usize {
235 self.0.len()
236 }
237
238 /// Returns the name in the type specific to the variant
239 /// (i.e. [`Qname`] or [`UriQualifiedName`]).
240 pub fn to_variant(&self) -> EqnameVariantData<&Qname, &UriQualifiedName> {
241 if Self::should_be_parsed_as_uri_qualified(self.as_str()) {
242 unsafe {
243 debug_assert!(
244 UriQualifiedName::from_str(self.as_str()).is_ok(),
245 "The string should be URIQualifiedName"
246 );
247 // This is safe because the string must be valid URIQualifiedName,
248 // if `Self::should_be_parsed_as_uri_qualified` returns `true`.
249 EqnameVariantData::UriQualified(UriQualifiedName::new_unchecked(self.as_str()))
250 }
251 } else {
252 unsafe {
253 debug_assert!(
254 Qname::from_str(self.as_str()).is_ok(),
255 "The string should be QName"
256 );
257 // This is safe because the string must be valid QName,
258 // if `Self::should_be_parsed_as_uri_qualified` returns `false`.
259 EqnameVariantData::Q(Qname::new_unchecked(self.as_str()))
260 }
261 }
262 }
263
264 /// Returns the string as `QName`, if valid.
265 pub fn as_qname(&self) -> Option<&Qname> {
266 if Self::should_be_parsed_as_uri_qualified(self.as_str()) {
267 return None;
268 }
269 unsafe {
270 debug_assert!(
271 Qname::from_str(self.as_str()).is_ok(),
272 "The string should be QName"
273 );
274 // This is safe because the string must be valid QName,
275 // if `Self::should_be_parsed_as_uri_qualified` returns `false`.
276 Some(Qname::new_unchecked(self.as_str()))
277 }
278 }
279
280 /// Returns the string as `URIQualifiedName`, if valid.
281 pub fn as_uri_qualified_name(&self) -> Option<&UriQualifiedName> {
282 if !Self::should_be_parsed_as_uri_qualified(self.as_str()) {
283 return None;
284 }
285 unsafe {
286 debug_assert!(
287 UriQualifiedName::from_str(self.as_str()).is_ok(),
288 "The string should be URIQualifiedName"
289 );
290 // This is safe because the string must be valid URIQualifiedName,
291 // if `Self::should_be_parsed_as_uri_qualified` returns `true`.
292 Some(UriQualifiedName::new_unchecked(self.as_str()))
293 }
294 }
295
296 /// Parses the leading `Eqname` and returns the value and the rest input.
297 ///
298 /// # Exmaples
299 ///
300 /// ```
301 /// # use xml_string::names::Eqname;
302 /// let input = "foo:bar:012";
303 /// let expected = Eqname::from_str("foo:bar")
304 /// .expect("valid QName");
305 /// assert_eq!(
306 /// Eqname::parse_next(input),
307 /// Ok((expected, ":012"))
308 /// );
309 /// # Ok::<_, xml_string::names::NameError>(())
310 /// ```
311 ///
312 /// ```
313 /// # use xml_string::names::Eqname;
314 /// let input = "Q{foo}bar:012";
315 /// let expected = Eqname::from_str("Q{foo}bar")
316 /// .expect("valid UriQualifiedName");
317 /// assert_eq!(
318 /// Eqname::parse_next(input),
319 /// Ok((expected, ":012"))
320 /// );
321 /// # Ok::<_, xml_string::names::NameError>(())
322 /// ```
323 ///
324 /// ```
325 /// # use xml_string::names::Eqname;
326 /// let input = "012";
327 /// assert!(Eqname::parse_next(input).is_err());
328 /// # Ok::<_, xml_string::names::NameError>(())
329 /// ```
330 pub fn parse_next(s: &str) -> Result<(&Self, &str), NameError> {
331 match Self::from_str(s) {
332 Ok(v) => Ok((v, &s[s.len()..])),
333 Err(e) if e.valid_up_to() == 0 => Err(e),
334 Err(e) => {
335 let valid_up_to = e.valid_up_to();
336 let v = unsafe {
337 let valid = &s[..valid_up_to];
338 debug_assert!(Self::validate(valid).is_ok());
339 // This is safe because the substring is valid.
340 Self::new_unchecked(valid)
341 };
342 Ok((v, &s[valid_up_to..]))
343 }
344 }
345 }
346
347 /// Returns the position where the local name starts.
348 ///
349 /// Note that this is O(length) operation.
350 fn local_name_start(&self) -> usize {
351 let s = self.as_str();
352 if Self::should_be_parsed_as_uri_qualified(s) {
353 s.find('}')
354 .expect("Should never fail: Valid URIQualifiedName contains '}' character")
355 + 1
356 } else {
357 s.find(':').map_or(0, |colon_pos| colon_pos + 1)
358 }
359 }
360
361 /// Returns the namespace part if available: prefix for [`Qname`], URI for [`UriQualifiedName`].
362 ///
363 /// Note that this is O(length) operation.
364 /// Consider using [`ParsedEqname::local_name`] method if possible.
365 ///
366 /// # Examples
367 ///
368 /// ```
369 /// # use xml_string::names::Eqname;
370 /// use xml_string::names::{EqnameNamespace, Ncname, Qname};
371 ///
372 /// let nc = Eqname::from_str("local")?;
373 /// assert_eq!(nc.namespace(), EqnameNamespace::None);
374 ///
375 /// let q = Eqname::from_str("foo:bar")?;
376 /// let q_prefix = Ncname::from_str("foo")
377 /// .expect("Should never fail: Valid NCName");
378 /// assert_eq!(q.namespace(), EqnameNamespace::Prefix(q_prefix));
379 ///
380 /// let uri_qualified = Eqname::from_str("Q{foo}bar")?;
381 /// assert_eq!(uri_qualified.namespace(), EqnameNamespace::Uri("foo"));
382 ///
383 /// let uri_qualified_empty = Eqname::from_str("Q{}bar")?;
384 /// assert_eq!(uri_qualified_empty.namespace(), EqnameNamespace::Uri(""));
385 /// # Ok::<_, xml_string::names::NameError>(())
386 /// ```
387 pub fn namespace(&self) -> EqnameNamespace<'_> {
388 match self.to_variant() {
389 EqnameVariantData::Q(q) => q
390 .prefix()
391 .map_or(EqnameNamespace::None, EqnameNamespace::Prefix),
392 EqnameVariantData::UriQualified(uri_qualified) => {
393 EqnameNamespace::Uri(uri_qualified.uri())
394 }
395 }
396 }
397
398 /// Returns the local name.
399 ///
400 /// Note that this is O(length) operation.
401 /// Consider using [`ParsedEqname::local_name`] method if possible.
402 ///
403 /// # Examples
404 ///
405 /// ```
406 /// # use xml_string::names::Eqname;
407 /// let q = Eqname::from_str("foo:bar")?;
408 /// assert_eq!(q.local_name(), "bar");
409 ///
410 /// let nc = Eqname::from_str("bar")?;
411 /// assert_eq!(nc.local_name(), "bar");
412 ///
413 /// let uri_qualified = Eqname::from_str("Q{foo}bar")?;
414 /// assert_eq!(uri_qualified.local_name(), "bar");
415 /// # Ok::<_, xml_string::names::NameError>(())
416 /// ```
417 #[inline]
418 #[must_use]
419 pub fn local_name(&self) -> &Ncname {
420 match self.to_variant() {
421 EqnameVariantData::Q(qname) => qname.local_part(),
422 EqnameVariantData::UriQualified(uri_qualified) => uri_qualified.local_name(),
423 }
424 }
425
426 /// Returns a pair of the namespace and the local name.
427 ///
428 /// This returns the same result as `(self.namespace(), self.local_name())`,
429 /// but more efficiently than calling [`Eqname::namespace`] and
430 /// [`Eqname::local_name`] individually.
431 ///
432 /// # Examples
433 ///
434 /// ```
435 /// # use xml_string::names::Eqname;
436 /// use xml_string::names::{EqnameNamespace, Ncname};
437 ///
438 /// let ncname_bar = Ncname::from_str("bar")
439 /// .expect("Should never fail: Valid NCName");
440 ///
441 /// let nc = Eqname::from_str("bar")?;
442 /// assert_eq!(nc.namespace_and_local(), (EqnameNamespace::None, ncname_bar));
443 ///
444 /// let q = Eqname::from_str("foo:bar")?;
445 /// let expected_prefix = Ncname::from_str("foo")
446 /// .expect("Should never fail: Valid NCName");
447 /// assert_eq!(
448 /// q.namespace_and_local(),
449 /// (EqnameNamespace::Prefix(expected_prefix), ncname_bar)
450 /// );
451 ///
452 /// let uri_qualified = Eqname::from_str("Q{foo}bar")?;
453 /// assert_eq!(uri_qualified.namespace_and_local(), (EqnameNamespace::Uri("foo"), ncname_bar));
454 /// # Ok::<_, xml_string::names::NameError>(())
455 /// ```
456 #[must_use]
457 pub fn namespace_and_local(&self) -> (EqnameNamespace<'_>, &'_ Ncname) {
458 match self.to_variant() {
459 EqnameVariantData::Q(q) => {
460 let (prefix, local) = q.prefix_and_local();
461 (
462 prefix.map_or(EqnameNamespace::None, EqnameNamespace::Prefix),
463 local,
464 )
465 }
466 EqnameVariantData::UriQualified(uri_qualified) => {
467 let (uri, local) = uri_qualified.uri_and_local();
468 (EqnameNamespace::Uri(uri), local)
469 }
470 }
471 }
472
473 /// Converts a `Box<Eqname>` into a `Box<str>` without copying or allocating.
474 ///
475 /// # Examples
476 ///
477 /// ```
478 /// # use xml_string::names::Eqname;
479 /// let name = Eqname::from_str("q:name")?;
480 /// let boxed_name: Box<Eqname> = name.into();
481 /// assert_eq!(&*boxed_name, name);
482 /// let boxed_str: Box<str> = boxed_name.into_boxed_str();
483 /// assert_eq!(&*boxed_str, name.as_str());
484 /// # Ok::<_, xml_string::names::NameError>(())
485 /// ```
486 #[cfg(feature = "alloc")]
487 pub fn into_boxed_str(self: alloc::boxed::Box<Self>) -> Box<str> {
488 unsafe {
489 // This is safe because `Eqname` has the same memory layout as `str`
490 // (thanks to `#[repr(transparent)]`).
491 alloc::boxed::Box::<str>::from_raw(alloc::boxed::Box::<Self>::into_raw(self) as *mut str)
492 }
493 }
494}
495
496impl_traits_for_custom_string_slice!(Eqname);
497
498impl<'a> From<&'a Ncname> for &'a Eqname {
499 #[inline]
500 fn from(s: &'a Ncname) -> Self {
501 s.as_ref()
502 }
503}
504
505impl<'a> From<&'a Qname> for &'a Eqname {
506 #[inline]
507 fn from(s: &'a Qname) -> Self {
508 s.as_ref()
509 }
510}
511
512impl<'a> From<&'a UriQualifiedName> for &'a Eqname {
513 #[inline]
514 fn from(s: &'a UriQualifiedName) -> Self {
515 s.as_ref()
516 }
517}
518
519impl<'a> From<ParsedEqname<'a>> for &'a Eqname {
520 #[inline]
521 fn from(s: ParsedEqname<'a>) -> Self {
522 s.as_eqname()
523 }
524}
525
526impl<'a> TryFrom<&'a str> for &'a Eqname {
527 type Error = NameError;
528
529 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
530 Eqname::validate(s)?;
531 Ok(unsafe {
532 // This is safe because the string is validated.
533 Eqname::new_unchecked(s)
534 })
535 }
536}
537
538impl<'a> TryFrom<&'a Eqname> for &'a Ncname {
539 type Error = NameError;
540
541 fn try_from(s: &'a Eqname) -> Result<Self, Self::Error> {
542 if let EqnameVariantData::Q(qname) = s.to_variant() {
543 Self::try_from(qname)
544 } else {
545 debug_assert!(s.as_str().starts_with("Q{"));
546 // `valid_up_to` is 1, because the string starts with "Q{".
547 Err(NameError::new(TargetNameType::Ncname, 1))
548 }
549 }
550}
551
552/// Parsed [`EQName`] reference.
553///
554/// [`EQName`]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#prod-xpath31-EQName
555#[derive(Clone, Copy, Eq)]
556pub struct ParsedEqname<'a> {
557 /// Content string.
558 content: EqnameVariantData<ParsedQname<'a>, ParsedUriQualifiedName<'a>>,
559}
560
561impl<'a> ParsedEqname<'a> {
562 /// Creates a new `ParsedEqname`.
563 ///
564 /// # Panics
565 ///
566 /// Panics if `local_name_start` points is invalid.
567 #[must_use]
568 fn new(content: &'a Eqname, local_name_start: usize) -> Self {
569 let content = match content.to_variant() {
570 EqnameVariantData::Q(qname) => {
571 let prefix_len = NonZeroUsize::new(local_name_start.saturating_sub(1));
572 EqnameVariantData::Q(ParsedQname::new(qname, prefix_len))
573 }
574 EqnameVariantData::UriQualified(uri_qualified_name) => {
575 let local_name_start = NonZeroUsize::new(local_name_start)
576 .expect("`local_name_start` should be nonzero");
577 EqnameVariantData::UriQualified(ParsedUriQualifiedName::new(
578 uri_qualified_name,
579 local_name_start,
580 ))
581 }
582 };
583 Self { content }
584 }
585
586 /// Creates a new `ParsedEqname<'_>` from the given string slice.
587 ///
588 /// # Failures
589 ///
590 /// Fails if the given string is not a valid [`EQName`].
591 ///
592 /// # Examples
593 ///
594 /// ```
595 /// # use xml_string::names::ParsedEqname;
596 /// let nc = ParsedEqname::from_str("hello")?;
597 /// assert_eq!(nc, "hello");
598 ///
599 /// let q = ParsedEqname::from_str("foo:bar")?;
600 /// assert_eq!(q, "foo:bar");
601 ///
602 /// let uri_qualified = ParsedEqname::from_str("Q{foo}bar")?;
603 /// assert_eq!(uri_qualified, "Q{foo}bar");
604 ///
605 /// assert!(ParsedEqname::from_str("").is_err(), "Empty string is not an EQName");
606 /// assert!(ParsedEqname::from_str("foo bar").is_err(), "Whitespace is not allowed");
607 /// assert!(ParsedEqname::from_str("foo:bar:baz").is_err(), "Two or more colons are not allowed");
608 /// assert!(ParsedEqname::from_str("0foo").is_err(), "ASCII digit at the beginning is not allowed");
609 /// # Ok::<_, xml_string::names::NameError>(())
610 /// ```
611 ///
612 /// [`EQName`]: https://www.w3.org/TR/2017/REC-xpath-31-20170321/#prod-xpath31-EQName
613 // `FromStr` can be implemented only for types with static lifetime.
614 #[allow(clippy::should_implement_trait)]
615 #[inline]
616 pub fn from_str(s: &'a str) -> Result<Self, NameError> {
617 Self::try_from(s)
618 }
619
620 /// Returns the string as `&Eqname`.
621 ///
622 /// # Exmaples
623 ///
624 /// ```
625 /// # use xml_string::names::ParsedEqname;
626 /// use xml_string::names::Eqname;
627 ///
628 /// let name = ParsedEqname::from_str("foo:bar")?;
629 /// assert_eq!(name, "foo:bar");
630 ///
631 /// let s: &Eqname = name.as_eqname();
632 /// assert_eq!(s, "foo:bar");
633 /// # Ok::<_, xml_string::names::NameError>(())
634 /// ```
635 #[inline]
636 #[must_use]
637 pub fn as_eqname(&self) -> &'a Eqname {
638 unsafe {
639 // Paranoia test.
640 debug_assert!(Eqname::from_str(self.as_str()).is_ok());
641 // This is safe because both of QName and URIQualifiedName is valid EQName.
642 Eqname::new_unchecked(self.as_str())
643 }
644 }
645
646 /// Returns the string as `&Qname` if it is QName.
647 ///
648 /// # Exmaples
649 ///
650 /// ```
651 /// # use xml_string::names::ParsedEqname;
652 /// use xml_string::names::Qname;
653 ///
654 /// let name = ParsedEqname::from_str("foo:bar")?;
655 /// assert_eq!(name, "foo:bar");
656 ///
657 /// let s: &Qname = name.as_qname()
658 /// .expect("The string is QName");
659 /// assert_eq!(s, "foo:bar");
660 /// # Ok::<_, xml_string::names::NameError>(())
661 /// ```
662 #[inline]
663 #[must_use]
664 pub fn as_qname(&self) -> Option<&'a Qname> {
665 match &self.content {
666 EqnameVariantData::Q(v) => Some(v.as_qname()),
667 EqnameVariantData::UriQualified(_) => None,
668 }
669 }
670
671 /// Returns the string as `&UriQualifiedName` if it is URIQualifiedName.
672 ///
673 /// # Exmaples
674 ///
675 /// ```
676 /// # use xml_string::names::ParsedEqname;
677 /// use xml_string::names::UriQualifiedName;
678 ///
679 /// let name = ParsedEqname::from_str("Q{foo}bar")?;
680 /// assert_eq!(name, "Q{foo}bar");
681 ///
682 /// let s: &UriQualifiedName = name.as_uri_qualified_name()
683 /// .expect("The string is URIQualifiedName");
684 /// assert_eq!(s, "Q{foo}bar");
685 /// # Ok::<_, xml_string::names::NameError>(())
686 /// ```
687 #[inline]
688 #[must_use]
689 pub fn as_uri_qualified_name(&self) -> Option<&'a UriQualifiedName> {
690 match &self.content {
691 EqnameVariantData::Q(_) => None,
692 EqnameVariantData::UriQualified(v) => Some(v.as_uri_qualified_name()),
693 }
694 }
695
696 /// Returns the string as `&str`.
697 ///
698 /// # Exmaples
699 ///
700 /// ```
701 /// # use xml_string::names::ParsedEqname;
702 /// let name = ParsedEqname::from_str("hello")?;
703 /// assert_eq!(name, "hello");
704 ///
705 /// let s: &str = name.as_str();
706 /// assert_eq!(s, "hello");
707 /// # Ok::<_, xml_string::names::NameError>(())
708 /// ```
709 #[inline]
710 #[must_use]
711 pub fn as_str(&self) -> &'a str {
712 match self.content {
713 EqnameVariantData::Q(v) => v.as_str(),
714 EqnameVariantData::UriQualified(v) => v.as_str(),
715 }
716 }
717
718 /// Returns the name in the type specific to the variant
719 /// (i.e. [`Qname`] or [`UriQualifiedName`]).
720 pub fn to_variant(&self) -> EqnameVariantData<&Qname, &UriQualifiedName> {
721 match self.content {
722 EqnameVariantData::Q(v) => EqnameVariantData::Q(v.as_qname()),
723 EqnameVariantData::UriQualified(v) => {
724 EqnameVariantData::UriQualified(v.as_uri_qualified_name())
725 }
726 }
727 }
728
729 /// Returns the name in the type specific to the variant
730 /// (i.e. [`ParsedQname`] or [`ParsedUriQualifiedName`]).
731 pub fn to_parsed_variant(
732 &self,
733 ) -> EqnameVariantData<&ParsedQname<'a>, &ParsedUriQualifiedName<'a>> {
734 self.content.as_ref()
735 }
736
737 /// Returns the namespace part if available: prefix for [`Qname`], URI for [`UriQualifiedName`].
738 ///
739 /// # Examples
740 ///
741 /// ```
742 /// # use xml_string::names::Eqname;
743 /// use xml_string::names::{EqnameNamespace, Ncname, Qname};
744 ///
745 /// let nc = Eqname::from_str("local")?;
746 /// assert_eq!(nc.namespace(), EqnameNamespace::None);
747 ///
748 /// let q = Eqname::from_str("foo:bar")?;
749 /// let q_prefix = Ncname::from_str("foo")
750 /// .expect("Should never fail: Valid NCName");
751 /// assert_eq!(q.namespace(), EqnameNamespace::Prefix(q_prefix));
752 ///
753 /// let uri_qualified = Eqname::from_str("Q{foo}bar")?;
754 /// assert_eq!(uri_qualified.namespace(), EqnameNamespace::Uri("foo"));
755 ///
756 /// let uri_qualified_empty = Eqname::from_str("Q{}bar")?;
757 /// assert_eq!(uri_qualified_empty.namespace(), EqnameNamespace::Uri(""));
758 /// # Ok::<_, xml_string::names::NameError>(())
759 /// ```
760 pub fn namespace(&self) -> EqnameNamespace<'a> {
761 match self.content {
762 EqnameVariantData::Q(q) => q
763 .prefix()
764 .map_or(EqnameNamespace::None, EqnameNamespace::Prefix),
765 EqnameVariantData::UriQualified(uri_qualified) => {
766 EqnameNamespace::Uri(uri_qualified.uri())
767 }
768 }
769 }
770
771 /// Returns the local part.
772 ///
773 /// # Examples
774 ///
775 /// ```
776 /// # use xml_string::names::ParsedEqname;
777 ///
778 /// let nc = ParsedEqname::from_str("foo")?;
779 /// assert_eq!(nc.local_name(), "foo");
780 /// let q = ParsedEqname::from_str("foo:bar")?;
781 /// assert_eq!(q.local_name(), "bar");
782 ///
783 /// let uri_qualified = ParsedEqname::from_str("Q{foo}bar")?;
784 /// assert_eq!(uri_qualified.local_name(), "bar");
785 /// # Ok::<_, xml_string::names::NameError>(())
786 /// ```
787 #[must_use]
788 pub fn local_name(&self) -> &'a Ncname {
789 match self.content {
790 EqnameVariantData::Q(v) => v.local_part(),
791 EqnameVariantData::UriQualified(v) => v.local_name(),
792 }
793 }
794
795 /// Returns a pair of the namespace and the local name.
796 ///
797 /// This returns the same result as `(self.namespace(), self.local_name())`,
798 /// but more efficiently than calling [`ParsedEqname::namespace`] and
799 /// [`ParsedEqname::local_name`] individually.
800 ///
801 /// # Examples
802 ///
803 /// ```
804 /// # use xml_string::names::ParsedEqname;
805 /// use xml_string::names::{EqnameNamespace, Ncname};
806 ///
807 /// let ncname_bar = Ncname::from_str("bar")
808 /// .expect("Should never fail: Valid NCName");
809 ///
810 /// let nc = ParsedEqname::from_str("bar")?;
811 /// assert_eq!(nc.namespace_and_local(), (EqnameNamespace::None, ncname_bar));
812 ///
813 /// let q = ParsedEqname::from_str("foo:bar")?;
814 /// let expected_prefix = Ncname::from_str("foo")
815 /// .expect("Should never fail: Valid NCName");
816 /// assert_eq!(
817 /// q.namespace_and_local(),
818 /// (EqnameNamespace::Prefix(expected_prefix), ncname_bar)
819 /// );
820 ///
821 /// let uri_qualified = ParsedEqname::from_str("Q{foo}bar")?;
822 /// assert_eq!(uri_qualified.namespace_and_local(), (EqnameNamespace::Uri("foo"), ncname_bar));
823 /// # Ok::<_, xml_string::names::NameError>(())
824 /// ```
825 #[must_use]
826 pub fn namespace_and_local(&self) -> (EqnameNamespace<'a>, &'a Ncname) {
827 match self.content {
828 EqnameVariantData::Q(q) => {
829 let (prefix, local) = q.prefix_and_local();
830 (
831 prefix.map_or(EqnameNamespace::None, EqnameNamespace::Prefix),
832 local,
833 )
834 }
835 EqnameVariantData::UriQualified(uri_qualified) => {
836 let (uri, local) = uri_qualified.uri_and_local();
837 (EqnameNamespace::Uri(uri), local)
838 }
839 }
840 }
841}
842
843impl PartialEq for ParsedEqname<'_> {
844 fn eq(&self, other: &Self) -> bool {
845 self.as_str() == other.as_str()
846 }
847}
848
849impl PartialOrd for ParsedEqname<'_> {
850 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
851 self.as_str().partial_cmp(other.as_str())
852 }
853}
854
855impl Ord for ParsedEqname<'_> {
856 fn cmp(&self, other: &Self) -> cmp::Ordering {
857 self.as_str().cmp(other.as_str())
858 }
859}
860
861impl hash::Hash for ParsedEqname<'_> {
862 fn hash<H: hash::Hasher>(&self, state: &mut H) {
863 self.as_str().hash(state)
864 }
865}
866
867impl PartialEq<str> for ParsedEqname<'_> {
868 #[inline]
869 fn eq(&self, other: &str) -> bool {
870 self.as_str() == other
871 }
872}
873impl_cmp!(str, ParsedEqname<'_>);
874
875impl PartialEq<&'_ str> for ParsedEqname<'_> {
876 #[inline]
877 fn eq(&self, other: &&str) -> bool {
878 self.as_str() == *other
879 }
880}
881impl_cmp!(&str, ParsedEqname<'_>);
882
883impl PartialEq<str> for &'_ ParsedEqname<'_> {
884 #[inline]
885 fn eq(&self, other: &str) -> bool {
886 self.as_str() == other
887 }
888}
889impl_cmp!(str, &ParsedEqname<'_>);
890
891#[cfg(feature = "alloc")]
892impl PartialEq<alloc::string::String> for ParsedEqname<'_> {
893 #[inline]
894 fn eq(&self, other: &alloc::string::String) -> bool {
895 self.as_str() == *other
896 }
897}
898#[cfg(feature = "alloc")]
899impl_cmp!(alloc::string::String, ParsedEqname<'_>);
900
901#[cfg(feature = "alloc")]
902impl PartialEq<&alloc::string::String> for ParsedEqname<'_> {
903 #[inline]
904 fn eq(&self, other: &&alloc::string::String) -> bool {
905 self.as_str() == **other
906 }
907}
908#[cfg(feature = "alloc")]
909impl_cmp!(&alloc::string::String, ParsedEqname<'_>);
910
911#[cfg(feature = "alloc")]
912impl PartialEq<alloc::boxed::Box<str>> for ParsedEqname<'_> {
913 #[inline]
914 fn eq(&self, other: &alloc::boxed::Box<str>) -> bool {
915 self.as_str() == other.as_ref()
916 }
917}
918#[cfg(feature = "alloc")]
919impl_cmp!(alloc::boxed::Box<str>, ParsedEqname<'_>);
920
921#[cfg(feature = "alloc")]
922impl PartialEq<alloc::borrow::Cow<'_, str>> for ParsedEqname<'_> {
923 #[inline]
924 fn eq(&self, other: &alloc::borrow::Cow<'_, str>) -> bool {
925 self.as_str() == *other
926 }
927}
928#[cfg(feature = "alloc")]
929impl_cmp!(alloc::borrow::Cow<'_, str>, ParsedEqname<'_>);
930
931impl AsRef<str> for ParsedEqname<'_> {
932 #[inline]
933 fn as_ref(&self) -> &str {
934 self.as_str()
935 }
936}
937
938impl AsRef<Eqname> for ParsedEqname<'_> {
939 fn as_ref(&self) -> &Eqname {
940 match &self.content {
941 EqnameVariantData::Q(qname) => qname.as_ref(),
942 EqnameVariantData::UriQualified(uri_qualified) => uri_qualified.as_ref(),
943 }
944 }
945}
946
947impl<'a> From<&'a Eqname> for ParsedEqname<'a> {
948 fn from(s: &'a Eqname) -> Self {
949 Self::new(s, s.local_name_start())
950 }
951}
952
953impl<'a> TryFrom<&'a str> for ParsedEqname<'a> {
954 type Error = NameError;
955
956 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
957 let local_name_start = match Eqname::parse_as_possible(s) {
958 Ok(EqnameVariantData::Q(start)) => start,
959 Ok(EqnameVariantData::UriQualified(start)) => start.get(),
960 Err(e) => {
961 return Err(NameError::new(
962 TargetNameType::Eqname,
963 e.map_or(0, |(_colon_pos, valid_up_to)| valid_up_to.get()),
964 ))
965 }
966 };
967 let content = unsafe {
968 // This is safe because the string is validated by
969 // `Eqname::parse_as_possible()`.
970 Eqname::new_unchecked(s)
971 };
972 Ok(Self::new(content, local_name_start))
973 }
974}
975
976impl fmt::Debug for ParsedEqname<'_> {
977 #[inline]
978 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
979 f.write_str(self.as_str())
980 }
981}
982
983impl fmt::Display for ParsedEqname<'_> {
984 #[inline]
985 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
986 f.write_str(self.as_str())
987 }
988}
989
990#[cfg(test)]
991mod tests {
992 use super::*;
993
994 fn ncname(s: &str) -> &Ncname {
995 Ncname::from_str(s)
996 .unwrap_or_else(|e| panic!("Failed to cerate Ncname from {:?}: {}", s, e))
997 }
998
999 fn eqname(s: &str) -> &Eqname {
1000 Eqname::from_str(s)
1001 .unwrap_or_else(|e| panic!("Failed to create Eqname from {:?}: {}", s, e))
1002 }
1003
1004 fn parsed_eqname(s: &str) -> ParsedEqname<'_> {
1005 ParsedEqname::from_str(s)
1006 .unwrap_or_else(|e| panic!("Failed to create ParsedEqname from {:?}: {}", s, e))
1007 }
1008
1009 fn ensure_eq(s: &str) {
1010 assert_eq!(
1011 Eqname::from_str(s).expect("Should not fail"),
1012 s,
1013 "String: {:?}",
1014 s
1015 );
1016 }
1017
1018 fn ensure_error_at(s: &str, valid_up_to: usize) {
1019 let err = Eqname::from_str(s).expect_err("Should fail");
1020 assert_eq!(err.valid_up_to(), valid_up_to, "String: {:?}", s);
1021 }
1022
1023 #[test]
1024 fn qname_str_valid() {
1025 ensure_eq("local");
1026 ensure_eq("foo:bar");
1027 ensure_eq("Q");
1028 }
1029
1030 #[test]
1031 fn qname_str_invalid() {
1032 ensure_error_at("", 0);
1033 ensure_error_at(":", 0);
1034 ensure_error_at("foo:", 3);
1035 ensure_error_at(":bar", 0);
1036 ensure_error_at("foo::bar", 3);
1037 ensure_error_at("foo:bar:", 7);
1038 ensure_error_at(":foo:bar", 0);
1039 ensure_error_at("foo:bar:baz", 7);
1040 }
1041
1042 #[test]
1043 fn uqname_str_valid() {
1044 ensure_eq("Q{}local");
1045 ensure_eq("Q{foo}bar");
1046 ensure_eq("Q{http://example.com/}local");
1047 }
1048
1049 #[test]
1050 fn uqname_str_invalid() {
1051 ensure_error_at("", 0);
1052 ensure_error_at("Q{", 1);
1053 ensure_error_at("Q{}", 1);
1054 ensure_error_at("Q{}:", 1);
1055 ensure_error_at("Q{}foo:", 6);
1056 ensure_error_at("Q{}foo:bar", 6);
1057 ensure_error_at("Q{foo}bar:baz", 9);
1058 ensure_error_at("Q{foo}bar}baz", 9);
1059 ensure_error_at("Q{foo{bar}baz", 1);
1060 }
1061
1062 #[test]
1063 fn parse_as_possible() {
1064 assert_eq!(
1065 Eqname::parse_as_possible("local"),
1066 Ok(EqnameVariantData::Q(0))
1067 );
1068 assert_eq!(
1069 Eqname::parse_as_possible("foo:bar"),
1070 Ok(EqnameVariantData::Q(4))
1071 );
1072
1073 assert_eq!(Eqname::parse_as_possible(""), Err(None));
1074
1075 assert_eq!(
1076 Eqname::parse_as_possible("foo:"),
1077 Err(Some(0).zip(NonZeroUsize::new(3)))
1078 );
1079 assert_eq!(
1080 Eqname::parse_as_possible("foo:bar:"),
1081 Err(Some(4).zip(NonZeroUsize::new(7)))
1082 );
1083 assert_eq!(
1084 Eqname::parse_as_possible("foo::bar"),
1085 Err(Some(0).zip(NonZeroUsize::new(3)))
1086 );
1087 assert_eq!(Eqname::parse_as_possible(":foo"), Err(None));
1088
1089 assert_eq!(
1090 Eqname::parse_as_possible("Q{}bar"),
1091 Ok(EqnameVariantData::UriQualified(
1092 NonZeroUsize::new(3).expect("Should never fail: not zero")
1093 ))
1094 );
1095 assert_eq!(
1096 Eqname::parse_as_possible("Q{foo}bar"),
1097 Ok(EqnameVariantData::UriQualified(
1098 NonZeroUsize::new(6).expect("Should never fail: not zero")
1099 ))
1100 );
1101
1102 assert_eq!(
1103 Eqname::parse_as_possible("Q{}foo:bar"),
1104 Err(Some(3).zip(NonZeroUsize::new(6)))
1105 );
1106 assert_eq!(
1107 Eqname::parse_as_possible("Q{foo}bar:baz"),
1108 Err(Some(6).zip(NonZeroUsize::new(9)))
1109 );
1110 }
1111
1112 #[test]
1113 fn parsed_eqname_from_str() {
1114 assert_eq!(
1115 ParsedEqname::from_str("foo").map(|v| v.as_eqname()),
1116 Ok(eqname("foo"))
1117 );
1118 assert_eq!(
1119 ParsedEqname::from_str("foo:bar").map(|v| v.as_eqname()),
1120 Ok(eqname("foo:bar"))
1121 );
1122 assert_eq!(
1123 ParsedEqname::from_str("Q{foo}bar").map(|v| v.as_eqname()),
1124 Ok(eqname("Q{foo}bar"))
1125 );
1126
1127 assert_eq!(
1128 ParsedEqname::from_str(""),
1129 Err(NameError::new(TargetNameType::Eqname, 0))
1130 );
1131 assert_eq!(
1132 ParsedEqname::from_str("foo::bar"),
1133 Err(NameError::new(TargetNameType::Eqname, 3))
1134 );
1135 assert_eq!(
1136 ParsedEqname::from_str("Q{foo}:bar"),
1137 Err(NameError::new(TargetNameType::Eqname, 1))
1138 );
1139 assert_eq!(
1140 ParsedEqname::from_str("Q{foo}bar:baz"),
1141 Err(NameError::new(TargetNameType::Eqname, 9))
1142 );
1143 }
1144
1145 #[test]
1146 fn parsed_eqname_namespace() {
1147 assert_eq!(parsed_eqname("local").namespace(), EqnameNamespace::None);
1148
1149 assert_eq!(
1150 parsed_eqname("foo:bar").namespace(),
1151 EqnameNamespace::Prefix(ncname("foo"))
1152 );
1153
1154 assert_eq!(
1155 parsed_eqname("Q{}foo").namespace(),
1156 EqnameNamespace::Uri("")
1157 );
1158 assert_eq!(
1159 parsed_eqname("Q{foo}bar").namespace(),
1160 EqnameNamespace::Uri("foo")
1161 );
1162 }
1163
1164 #[test]
1165 fn parsed_uri_qualified_name_local_name() {
1166 assert_eq!(parsed_eqname("local").local_name(), ncname("local"));
1167 assert_eq!(parsed_eqname("foo:bar").local_name(), ncname("bar"));
1168 assert_eq!(parsed_eqname("Q{}foo").local_name(), ncname("foo"));
1169 assert_eq!(parsed_eqname("Q{foo}bar").local_name(), ncname("bar"));
1170 }
1171
1172 #[test]
1173 fn parsed_eqname_namespace_and_local() {
1174 assert_eq!(
1175 parsed_eqname("local").namespace_and_local(),
1176 (EqnameNamespace::None, ncname("local"))
1177 );
1178 assert_eq!(
1179 parsed_eqname("foo:bar").namespace_and_local(),
1180 (EqnameNamespace::Prefix(ncname("foo")), ncname("bar"))
1181 );
1182 assert_eq!(
1183 parsed_eqname("Q{}foo").namespace_and_local(),
1184 (EqnameNamespace::Uri(""), ncname("foo"))
1185 );
1186 assert_eq!(
1187 parsed_eqname("Q{foo}bar").namespace_and_local(),
1188 (EqnameNamespace::Uri("foo"), ncname("bar"))
1189 );
1190 }
1191}