xot/xmlname/
reference.rs

1use std::borrow::Cow;
2
3use crate::id::{NameId, NamespaceId, PrefixId};
4use crate::xotdata::Xot;
5use crate::{Error, Node};
6
7use super::owned::OwnedName;
8
9/// Name string information for an xml name.
10pub trait NameStrInfo {
11    /// Access the local name as a string reference
12    fn local_name(&self) -> &str;
13
14    /// Get the namespace uri as a str reference.
15    ///
16    /// If there is no namespace, this is the empty string.
17    fn namespace(&self) -> &str;
18
19    /// Access the prefix as a string
20    fn prefix(&self) -> &str;
21
22    /// Access the full name as a string
23    fn full_name(&self) -> Cow<str> {
24        let prefix = self.prefix();
25        if !prefix.is_empty() {
26            Cow::Owned(format!("{}:{}", prefix, self.local_name()))
27        } else {
28            Cow::Borrowed(self.local_name())
29        }
30    }
31}
32
33/// The most complete way to access name information, backed by Xot. This is a
34/// reference and cannot be created directly.
35///
36/// You can create one by using [`OwnedName`], or by using the
37/// [`Xot::name_ref`] and [`Xot::node_name_ref`] methods.
38///
39/// You can access name string information using the [`NameStrInfo`] trait.
40///
41/// It can also be used directly to create new elements and attributes, instead
42/// of a [`crate::NameId`].
43#[derive(Debug, Clone)]
44pub struct RefName<'a> {
45    /// Looking up string information for names, namespaces and prefixes.
46    xot: &'a Xot,
47    // This identifies the name and namespace. This is the only thing that
48    // identifies the xml name and is used for hashing and comparison.
49    name_id: NameId,
50    /// We also keep track of the prefix id
51    prefix_id: PrefixId,
52}
53
54impl std::hash::Hash for RefName<'_> {
55    #[inline]
56    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
57        self.name_id.hash(state);
58    }
59}
60
61impl PartialEq for RefName<'_> {
62    #[inline]
63    fn eq(&self, other: &Self) -> bool {
64        self.name_id == other.name_id
65    }
66}
67
68impl Eq for RefName<'_> {}
69
70impl<'a> NameStrInfo for RefName<'a> {
71    #[inline]
72    fn local_name(&self) -> &'a str {
73        self.xot.local_name_str(self.name_id)
74    }
75
76    #[inline]
77    fn namespace(&self) -> &'a str {
78        self.xot.namespace_str(self.namespace_id())
79    }
80
81    #[inline]
82    fn prefix(&self) -> &'a str {
83        let prefix_id = self.prefix_id();
84        self.xot.prefix_str(prefix_id)
85    }
86}
87
88impl<'a> RefName<'a> {
89    pub(crate) fn new(xot: &'a Xot, name_id: NameId, prefix_id: PrefixId) -> Self {
90        Self {
91            xot,
92            name_id,
93            prefix_id,
94        }
95    }
96
97    /// Get the Xot name id.
98    #[inline]
99    pub fn name_id(&self) -> NameId {
100        self.name_id
101    }
102
103    /// Get the Xot namespace id.
104    #[inline]
105    pub fn namespace_id(&self) -> NamespaceId {
106        self.xot.namespace_for_name(self.name_id)
107    }
108
109    /// Get the Xot prefix id.
110    #[inline]
111    pub fn prefix_id(&self) -> PrefixId {
112        self.prefix_id
113    }
114
115    pub(crate) fn from_node(xot: &'a Xot, node: Node, name_id: NameId) -> Result<Self, Error> {
116        let namespace_id = xot.namespace_for_name(name_id);
117        let prefix_id = if namespace_id != xot.no_namespace() {
118            xot.prefix_for_namespace(node, namespace_id)
119                .ok_or_else(|| Error::MissingPrefix(xot.namespace_str(namespace_id).to_string()))?
120        } else {
121            xot.empty_prefix()
122        };
123        Ok(Self::new(xot, name_id, prefix_id))
124    }
125
126    /// Create a new [`OwnedName`] from this reference.
127    ///
128    /// Normally you shouldn't have to do this because you can already access
129    /// the name string information on this reference using [`NameStrInfo`].
130    pub fn to_owned(&self) -> OwnedName {
131        OwnedName::new(
132            self.local_name().to_string(),
133            self.namespace().to_string(),
134            self.prefix().to_string(),
135        )
136    }
137
138    /// Has a namespace but no prefix, so it's in a `xmlns` namespace.
139    pub fn has_unprefixed_namespace(&self) -> bool {
140        self.namespace_id() != self.xot.no_namespace()
141            && self.xot.empty_prefix() == self.prefix_id()
142    }
143}
144
145impl<'a> From<RefName<'a>> for NameId {
146    #[inline]
147    fn from(name: RefName<'a>) -> Self {
148        name.name_id
149    }
150}