1use std::fmt::Debug;
2
3use ahash::AHashMap;
4
5use crate::error::Error;
6use crate::id::{NameId, NamespaceId, PrefixId};
7
8#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
17pub enum ValueType {
18 Document,
21 Element,
23 Text,
25 ProcessingInstruction,
27 Comment,
29 Attribute,
31 Namespace,
33}
34
35#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
36pub(crate) enum ValueCategory {
37 Normal,
38 Attribute,
39 Namespace,
40}
41
42#[derive(Debug, Clone, Eq, PartialEq, Hash)]
47pub enum Value {
48 Document,
51 Element(Element),
53 Text(Text),
55 ProcessingInstruction(ProcessingInstruction),
57 Comment(Comment),
59 Attribute(Attribute),
61 Namespace(Namespace),
63}
64
65impl Value {
66 pub fn value_type(&self) -> ValueType {
68 match self {
69 Value::Document => ValueType::Document,
70 Value::Element(_) => ValueType::Element,
71 Value::Text(_) => ValueType::Text,
72 Value::Comment(_) => ValueType::Comment,
73 Value::ProcessingInstruction(_) => ValueType::ProcessingInstruction,
74 Value::Attribute(_) => ValueType::Attribute,
75 Value::Namespace(_) => ValueType::Namespace,
76 }
77 }
78
79 pub(crate) fn value_category(&self) -> ValueCategory {
80 match self {
81 Value::Document
82 | Value::Element(_)
83 | Value::Text(_)
84 | Value::ProcessingInstruction(_)
85 | Value::Comment(_) => ValueCategory::Normal,
86 Value::Attribute(_) => ValueCategory::Attribute,
87 Value::Namespace(_) => ValueCategory::Namespace,
88 }
89 }
90
91 pub(crate) fn is_normal(&self) -> bool {
92 matches!(
93 self,
94 Value::Document
95 | Value::Element(_)
96 | Value::Text(_)
97 | Value::ProcessingInstruction(_)
98 | Value::Comment(_)
99 )
100 }
101}
102
103pub type Prefixes = AHashMap<PrefixId, NamespaceId>;
110
111#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
118pub struct Element {
119 pub(crate) name_id: NameId,
120}
121
122impl Element {
123 pub(crate) fn new(name_id: NameId) -> Self {
124 Self { name_id }
125 }
126
127 pub fn name(&self) -> NameId {
143 self.name_id
144 }
145
146 pub fn set_name(&mut self, name_id: NameId) {
148 self.name_id = name_id;
149 }
150}
151
152#[derive(Debug, Clone, Eq, PartialEq, Hash)]
156pub struct Text {
157 pub(crate) text: String,
158}
159
160impl Text {
161 pub(crate) fn new(text: String) -> Self {
162 Text { text }
163 }
164
165 pub fn get(&self) -> &str {
170 &self.text
171 }
172
173 pub fn get_mut(&mut self) -> &mut String {
175 &mut self.text
176 }
177
178 pub fn set<S: Into<String>>(&mut self, text: S) {
195 self.text = text.into();
196 }
197}
198
199#[derive(Debug, Clone, Eq, PartialEq, Hash)]
203pub struct Comment {
204 pub(crate) text: String,
205}
206
207impl Comment {
208 pub(crate) fn new(text: String) -> Self {
209 Comment { text }
210 }
211
212 pub fn get(&self) -> &str {
214 &self.text
215 }
216
217 pub fn set<S: Into<String>>(&mut self, text: S) -> Result<(), Error> {
221 let text = text.into();
222 if text.contains("--") {
223 return Err(Error::InvalidComment(text));
224 }
225 self.text = text;
226 Ok(())
227 }
228}
229
230#[derive(Debug, Clone, Eq, PartialEq, Hash)]
234pub struct ProcessingInstruction {
235 pub(crate) target: NameId,
236 pub(crate) data: Option<String>,
237}
238
239impl ProcessingInstruction {
240 pub(crate) fn new(target: NameId, data: Option<String>) -> Self {
241 ProcessingInstruction { target, data }
242 }
243
244 pub fn target(&self) -> NameId {
246 self.target
247 }
248
249 pub fn data(&self) -> Option<&str> {
251 self.data.as_deref()
252 }
253
254 pub fn set_target<S: Into<String>>(&mut self, target: NameId) -> Result<(), Error> {
256 self.target = target;
258 Ok(())
259 }
260
261 pub fn set_data<S: Into<String>>(&mut self, data: Option<S>) {
263 if let Some(data) = data {
266 let data = data.into();
267 if !data.is_empty() {
268 self.data = Some(data);
269 return;
270 }
271 }
272 self.data = None;
273 }
274}
275
276#[derive(Debug, Clone, Eq, PartialEq, Hash)]
278pub struct Namespace {
279 pub(crate) prefix_id: PrefixId,
280 pub(crate) namespace_id: NamespaceId,
281}
282
283impl Namespace {
284 pub fn prefix(&self) -> PrefixId {
286 self.prefix_id
287 }
288
289 pub fn namespace(&self) -> NamespaceId {
291 self.namespace_id
292 }
293
294 pub fn set_namespace(&mut self, namespace_id: NamespaceId) {
296 self.namespace_id = namespace_id;
297 }
298}
299
300#[derive(Debug, Clone, Eq, PartialEq, Hash)]
302pub struct Attribute {
303 pub(crate) name_id: NameId,
304 pub(crate) value: String,
305}
306
307impl Attribute {
308 pub fn name(&self) -> NameId {
310 self.name_id
311 }
312
313 pub fn value(&self) -> &str {
315 &self.value
316 }
317
318 pub fn set_value<S: Into<String>>(&mut self, value: S) {
320 self.value = value.into();
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327 use crate::xotdata::Xot;
328
329 #[test]
330 fn test_element_hashable_name() {
331 let mut xot = Xot::new();
332 let a = xot.add_name("a");
333 let b = xot.add_name("b");
334
335 let alpha = Element { name_id: a };
336 let beta = Element { name_id: a };
337 let gamma = Element { name_id: b };
338
339 let hash_builder = ahash::RandomState::with_seed(42);
340 let alpha_hash = hash_builder.hash_one(alpha);
341 let beta_hash = hash_builder.hash_one(beta);
342 let gamma_hash = hash_builder.hash_one(gamma);
343 assert_eq!(alpha_hash, beta_hash);
344 assert_ne!(alpha_hash, gamma_hash);
345 }
346}