xot/xmlname/
owned.rs

1use crate::{Error, Xot};
2
3use super::CreateName;
4use super::{reference::NameStrInfo, RefName};
5
6/// An owned name stores the name, namespace and prefix as owned strings.
7///
8/// An owned name is handy when you don't want to depend on Xot, or for
9/// debugging.
10///
11/// It cannot be used to create elements directly, but you can turn this into a
12/// [`CreateName`] using [`OwnedName::to_create`] and a [`RefName`] using
13/// [`OwnedName::to_ref`].
14///
15/// You can access name string information using the [`NameStrInfo`] trait.
16///
17/// If you enable the `serde` feature it can be serialized and deserialized.
18#[derive(Debug, Clone)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct OwnedName {
21    // fields are postfixed _str so they don't conflict with the methods in
22    // NameStrInfo
23    local_name_str: String,
24    // the empty namespace uri means no namespace
25    namespace_str: String,
26    // the empty prefix means no prefix.
27    prefix_str: String,
28}
29
30impl std::hash::Hash for OwnedName {
31    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
32        self.local_name_str.hash(state);
33        self.namespace_str.hash(state);
34    }
35}
36
37impl PartialEq for OwnedName {
38    fn eq(&self, other: &Self) -> bool {
39        self.local_name_str == other.local_name_str && self.namespace_str == other.namespace_str
40    }
41}
42
43impl Eq for OwnedName {}
44
45impl NameStrInfo for OwnedName {
46    fn local_name(&self) -> &str {
47        &self.local_name_str
48    }
49
50    fn namespace(&self) -> &str {
51        &self.namespace_str
52    }
53
54    fn prefix(&self) -> &str {
55        &self.prefix_str
56    }
57}
58
59impl OwnedName {
60    /// Create a new owned name.
61    pub fn new(local_name: String, namespace: String, prefix: String) -> Self {
62        Self {
63            local_name_str: local_name,
64            namespace_str: namespace,
65            prefix_str: prefix,
66        }
67    }
68
69    /// Create a name without a namespace
70    pub fn name(local_name: &str) -> Self {
71        Self {
72            local_name_str: local_name.to_string(),
73            namespace_str: String::new(),
74            prefix_str: String::new(),
75        }
76    }
77
78    /// Create a new name in a namespace, look up prefix information.
79    pub fn namespaced(
80        local_name: String,
81        namespace: String,
82        prefix_lookup: impl Fn(&str) -> Option<String>,
83    ) -> Result<Self, Error> {
84        let prefix =
85            prefix_lookup(&namespace).ok_or_else(|| Error::MissingPrefix(namespace.clone()))?;
86        Ok(Self {
87            local_name_str: local_name,
88            namespace_str: namespace,
89            prefix_str: prefix,
90        })
91    }
92
93    /// Create a new owned name from a prefix and a name.
94    pub fn prefixed(
95        prefix: &str,
96        local_name: &str,
97        lookup_namespace: impl Fn(&str) -> Option<String>,
98    ) -> Result<Self, Error> {
99        let namespace =
100            lookup_namespace(prefix).ok_or_else(|| Error::UnknownPrefix(prefix.to_string()))?;
101        Ok(Self {
102            local_name_str: local_name.to_string(),
103            namespace_str: namespace,
104            prefix_str: prefix.to_string(),
105        })
106    }
107
108    /// Given a fullname (with potentially a prefix), construct an XmlNameOwned
109    ///
110    /// This requires a function that can look up the namespace for a prefix.
111    pub fn parse_full_name(
112        full_name: &str,
113        lookup_namespace: impl Fn(&str) -> Option<String>,
114    ) -> Result<Self, Error> {
115        let (prefix, local_name) = parse_full_name(full_name);
116        Self::prefixed(prefix, local_name, lookup_namespace)
117    }
118
119    /// Convert this name into a name adding a * suffix.
120    ///
121    /// This can be useful to help generate unique names.
122    pub fn with_suffix(self) -> Self {
123        let mut local_name = self.local_name_str;
124        local_name.push('*');
125        Self {
126            local_name_str: local_name,
127            namespace_str: self.namespace_str,
128            prefix_str: self.prefix_str,
129        }
130    }
131
132    /// Convert this name into a name that's in a particular namespace.
133    ///
134    /// This only changes the namespace if there is an empty prefix and the
135    /// namespace is not set (the empty string).
136    pub fn with_default_namespace(self, namespace: &str) -> Self {
137        if !self.prefix_str.is_empty() || !self.namespace_str.is_empty() {
138            return self;
139        }
140        Self {
141            local_name_str: self.local_name_str,
142            namespace_str: namespace.to_string(),
143            prefix_str: self.prefix_str,
144        }
145    }
146
147    /// Name is in a namespace but without a prefix, so it's in a default namespace.
148    pub fn in_default_namespace(&self) -> bool {
149        !self.namespace_str.is_empty() && self.prefix_str.is_empty()
150    }
151
152    /// Create a new [`RefName`] from this owned name.
153    pub fn to_ref<'a>(&self, xot: &'a mut Xot) -> RefName<'a> {
154        let prefix_id = xot.add_prefix(&self.prefix_str);
155        let namespace_id = xot.add_namespace(&self.namespace_str);
156        let name_id = xot.add_name_ns(&self.local_name_str, namespace_id);
157        RefName::new(xot, name_id, prefix_id)
158    }
159
160    /// Create a new [`RefName`] only if names already exists in Xot.
161    ///
162    /// If the local name and namespace don't exist in Xot, returns None.
163    ///
164    /// This lets you pass in an immutable reference to `Xot`, unlike
165    /// `Owned::to_ref`.
166    ///
167    /// Ignores prefix information - even if the prefix doesn't exist, we still
168    /// get a ref name, just without a prefix. This is because the prefix
169    /// information is irrelevant for comparison.
170    ///
171    /// Otherwise, returns None.
172    pub fn maybe_to_ref<'a>(&self, xot: &'a Xot) -> Option<RefName<'a>> {
173        let prefix_id = xot.prefix(&self.prefix_str).unwrap_or(xot.empty_prefix());
174        let namespace_id = xot.namespace(&self.namespace_str)?;
175        let name_id = xot.name_ns(&self.local_name_str, namespace_id)?;
176        Some(RefName::new(xot, name_id, prefix_id))
177    }
178
179    /// Create a new [`CreateName`] name from this owned name.
180    ///
181    /// This disregards the prefix information.
182    pub fn to_create(&self, xot: &mut Xot) -> CreateName {
183        let namespace_id = xot.add_namespace(&self.namespace_str);
184        let name_id = xot.add_name_ns(&self.local_name_str, namespace_id);
185        CreateName::new(name_id)
186    }
187}
188
189/// TODO: fancier parser that validates the name
190pub(crate) fn parse_full_name(full_name: &str) -> (&str, &str) {
191    match full_name.find(':') {
192        Some(pos) => {
193            let (prefix, local_name) = full_name.split_at(pos);
194            (prefix, &local_name[1..])
195        }
196        None => ("", full_name),
197    }
198}