use std::fmt::Debug;
use ahash::AHashMap;
use crate::error::Error;
use crate::id::{NameId, NamespaceId, PrefixId};
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum ValueType {
Document,
Element,
Text,
ProcessingInstruction,
Comment,
Attribute,
Namespace,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub(crate) enum ValueCategory {
Normal,
Attribute,
Namespace,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Value {
Document,
Element(Element),
Text(Text),
ProcessingInstruction(ProcessingInstruction),
Comment(Comment),
Attribute(Attribute),
Namespace(Namespace),
}
impl Value {
pub fn value_type(&self) -> ValueType {
match self {
Value::Document => ValueType::Document,
Value::Element(_) => ValueType::Element,
Value::Text(_) => ValueType::Text,
Value::Comment(_) => ValueType::Comment,
Value::ProcessingInstruction(_) => ValueType::ProcessingInstruction,
Value::Attribute(_) => ValueType::Attribute,
Value::Namespace(_) => ValueType::Namespace,
}
}
pub(crate) fn value_category(&self) -> ValueCategory {
match self {
Value::Document
| Value::Element(_)
| Value::Text(_)
| Value::ProcessingInstruction(_)
| Value::Comment(_) => ValueCategory::Normal,
Value::Attribute(_) => ValueCategory::Attribute,
Value::Namespace(_) => ValueCategory::Namespace,
}
}
pub(crate) fn is_normal(&self) -> bool {
matches!(
self,
Value::Document
| Value::Element(_)
| Value::Text(_)
| Value::ProcessingInstruction(_)
| Value::Comment(_)
)
}
}
pub type Prefixes = AHashMap<PrefixId, NamespaceId>;
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub struct Element {
pub(crate) name_id: NameId,
}
impl Element {
pub(crate) fn new(name_id: NameId) -> Self {
Self { name_id }
}
pub fn name(&self) -> NameId {
self.name_id
}
pub fn set_name(&mut self, name_id: NameId) {
self.name_id = name_id;
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Text {
pub(crate) text: String,
}
impl Text {
pub(crate) fn new(text: String) -> Self {
Text { text }
}
pub fn get(&self) -> &str {
&self.text
}
pub fn get_mut(&mut self) -> &mut String {
&mut self.text
}
pub fn set<S: Into<String>>(&mut self, text: S) {
self.text = text.into();
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Comment {
pub(crate) text: String,
}
impl Comment {
pub(crate) fn new(text: String) -> Self {
Comment { text }
}
pub fn get(&self) -> &str {
&self.text
}
pub fn set<S: Into<String>>(&mut self, text: S) -> Result<(), Error> {
let text = text.into();
if text.contains("--") {
return Err(Error::InvalidComment(text));
}
self.text = text;
Ok(())
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct ProcessingInstruction {
pub(crate) target: NameId,
pub(crate) data: Option<String>,
}
impl ProcessingInstruction {
pub(crate) fn new(target: NameId, data: Option<String>) -> Self {
ProcessingInstruction { target, data }
}
pub fn target(&self) -> NameId {
self.target
}
pub fn data(&self) -> Option<&str> {
self.data.as_deref()
}
pub fn set_target<S: Into<String>>(&mut self, target: NameId) -> Result<(), Error> {
self.target = target;
Ok(())
}
pub fn set_data<S: Into<String>>(&mut self, data: Option<S>) {
if let Some(data) = data {
let data = data.into();
if !data.is_empty() {
self.data = Some(data);
return;
}
}
self.data = None;
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Namespace {
pub(crate) prefix_id: PrefixId,
pub(crate) namespace_id: NamespaceId,
}
impl Namespace {
pub fn prefix(&self) -> PrefixId {
self.prefix_id
}
pub fn namespace(&self) -> NamespaceId {
self.namespace_id
}
pub fn set_namespace(&mut self, namespace_id: NamespaceId) {
self.namespace_id = namespace_id;
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Attribute {
pub(crate) name_id: NameId,
pub(crate) value: String,
}
impl Attribute {
pub fn name(&self) -> NameId {
self.name_id
}
pub fn value(&self) -> &str {
&self.value
}
pub fn set_value<S: Into<String>>(&mut self, value: S) {
self.value = value.into();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::xotdata::Xot;
#[test]
fn test_element_hashable_name() {
let mut xot = Xot::new();
let a = xot.add_name("a");
let b = xot.add_name("b");
let alpha = Element { name_id: a };
let beta = Element { name_id: a };
let gamma = Element { name_id: b };
let hash_builder = ahash::RandomState::with_seed(42);
let alpha_hash = hash_builder.hash_one(alpha);
let beta_hash = hash_builder.hash_one(beta);
let gamma_hash = hash_builder.hash_one(gamma);
assert_eq!(alpha_hash, beta_hash);
assert_ne!(alpha_hash, gamma_hash);
}
}