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}