#![doc = include_str!("../README.md")]
use core::convert::From;
use std::{
collections::HashMap,
error::Error,
fmt::{self, Display},
str::FromStr,
};
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{self, SeqAccess, Visitor},
ser::SerializeSeq,
};
use void::Void;
use html_escape::encode_unquoted_attribute;
#[cfg(test)]
use serde_test::{Token, assert_de_tokens, assert_tokens};
#[derive(Debug, PartialEq, Clone)]
pub enum Element {
Tag {
name: String,
attributes: HashMap<String, AttributeValue>,
element_list: Vec<Element>,
},
String(String),
}
impl Default for Element {
fn default() -> Self {
Element::String("".to_string())
}
}
impl Serialize for Element {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Element::Tag {
name,
attributes,
element_list,
} => {
let mut seq =
serializer.serialize_seq(Some(1 + attributes.len() + element_list.len()))?;
seq.serialize_element(&name)?;
if !attributes.is_empty() {
seq.serialize_element(attributes)?;
}
if !element_list.is_empty() {
for element in element_list {
seq.serialize_element(&element)?;
}
}
seq.end()
}
Element::String(string) => serializer.serialize_str(string),
}
}
}
struct ElementVisitor;
impl<'de> Visitor<'de> for ElementVisitor {
type Value = Element;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("JsonML element, which is tag or string")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
Ok(Element::String(v.to_string()))
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
if let Some(name) = seq.next_element::<String>()? {
let mut attributes = HashMap::default();
let mut element_list = vec![];
#[derive(Deserialize)]
#[serde(untagged)]
enum AttributesOrElement {
Attributes(HashMap<String, AttributeValue>),
Element(Element),
}
if let Some(attributes_or_element) = seq.next_element::<AttributesOrElement>()? {
match attributes_or_element {
AttributesOrElement::Attributes(attrs) => attributes = attrs,
AttributesOrElement::Element(element) => element_list.push(element),
}
}
while let Some(element) = seq.next_element::<Element>()? {
element_list.push(element);
}
Ok(Element::Tag {
name,
attributes,
element_list,
})
} else {
Err(de::Error::missing_field("name"))
}
}
}
impl FromStr for Element {
type Err = Void;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Element::String(s.to_string()))
}
}
impl Display for Element {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Element::Tag {
name,
attributes,
element_list,
} => {
if !name.chars().all(|c| c.is_ascii_alphanumeric()) {
return Err(fmt::Error);
}
let attrs = if attributes.is_empty() {
"".to_string()
} else {
let mut attrs = String::default();
for (key, value) in attributes {
if !key.chars().all(|c| {
c != '\u{0020}'
&& c != '\u{0022}'
&& c != '\u{0027}'
&& c != '\u{003E}'
&& c != '\u{002F}'
&& c != '\u{003D}'
&& !('\u{FDD0}'..='\u{FDEF}').contains(&c)
&& c != '\u{FFFE}'
&& c != '\u{FFFF}'
&& c != '\u{1FFFE}'
&& c != '\u{1FFFF}'
&& c != '\u{2FFFE}'
&& c != '\u{2FFFF}'
&& c != '\u{3FFFE}'
&& c != '\u{3FFFF}'
&& c != '\u{4FFFE}'
&& c != '\u{4FFFF}'
&& c != '\u{5FFFE}'
&& c != '\u{5FFFF}'
&& c != '\u{6FFFE}'
&& c != '\u{6FFFF}'
&& c != '\u{7FFFE}'
&& c != '\u{7FFFF}'
&& c != '\u{8FFFE}'
&& c != '\u{8FFFF}'
&& c != '\u{9FFFE}'
&& c != '\u{9FFFF}'
&& c != '\u{AFFFE}'
&& c != '\u{AFFFF}'
&& c != '\u{BFFFE}'
&& c != '\u{BFFFF}'
&& c != '\u{CFFFE}'
&& c != '\u{CFFFF}'
&& c != '\u{DFFFE}'
&& c != '\u{DFFFF}'
&& c != '\u{EFFFE}'
&& c != '\u{EFFFF}'
&& c != '\u{FFFFE}'
&& c != '\u{FFFFF}'
&& c != '\u{10FFFE}'
&& c != '\u{10FFFF}'
}) {
return Err(fmt::Error);
}
let pair = format!(r#" {key}="{value}""#);
attrs += &pair;
}
attrs
};
let elms = element_list
.iter()
.map(|elm| elm.to_string())
.collect::<Vec<_>>()
.join("");
format!("<{name}{attrs}>{elms}</{name}>")
}
Element::String(s) => s.to_owned(),
}
)
}
}
#[test]
fn test_display_element() {
assert_eq!(
Element::Tag {
name: "div".to_string(),
attributes: HashMap::from([(
"id".to_string(),
AttributeValue::String("aaa".to_string())
),]),
element_list: vec![
Element::String("bbb".to_string()),
Element::Tag {
name: "span".to_string(),
attributes: HashMap::default(),
element_list: vec![Element::String("ccc".to_string())]
}
]
}
.to_string(),
r#"<div id="aaa">bbb<span>ccc</span></div>"#.to_string()
);
}
#[test]
#[should_panic]
fn test_display_element_invalid_tag_name() {
Element::Tag {
name: "あ".to_string(),
attributes: HashMap::default(),
element_list: vec![],
}
.to_string();
}
#[test]
#[should_panic]
fn test_display_element_invalid_attribute_name() {
Element::Tag {
name: "a".to_string(),
attributes: HashMap::from([(" ".to_string(), AttributeValue::Null)]),
element_list: vec![],
}
.to_string();
}
#[test]
fn test_display_element_encode_attribute_value() {
assert_eq!(
Element::Tag {
name: "a".to_string(),
attributes: HashMap::from(
[("b".to_string(), AttributeValue::String("=".to_string())),]
),
element_list: vec![]
}
.to_string(),
r#"<a b="="></a>"#.to_string()
);
}
impl<'de> Deserialize<'de> for Element {
fn deserialize<D>(deserializer: D) -> Result<Element, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(ElementVisitor)
}
}
#[test]
fn test_element_tag() {
let element = Element::Tag {
name: "li".to_string(),
attributes: HashMap::from([(
"style".to_string(),
AttributeValue::String("color:red".to_string()),
)]),
element_list: vec![Element::String("First Item".to_string())],
};
assert_tokens(
&element,
&[
Token::Seq { len: Some(3) },
Token::Str("li"),
Token::Map { len: Some(1) },
Token::Str("style"),
Token::Str("color:red"),
Token::MapEnd,
Token::Str("First Item"),
Token::SeqEnd,
],
);
}
#[test]
fn test_element_tag_name_only() {
let element = Element::Tag {
name: "li".to_string(),
attributes: HashMap::default(),
element_list: vec![],
};
assert_tokens(
&element,
&[Token::Seq { len: Some(1) }, Token::Str("li"), Token::SeqEnd],
);
}
#[test]
fn test_element_tag_without_element_list() {
let element = Element::Tag {
name: "li".to_string(),
attributes: HashMap::from([(
"style".to_string(),
AttributeValue::String("color:red".to_string()),
)]),
element_list: vec![],
};
assert_tokens(
&element,
&[
Token::Seq { len: Some(2) },
Token::Str("li"),
Token::Map { len: Some(1) },
Token::Str("style"),
Token::Str("color:red"),
Token::MapEnd,
Token::SeqEnd,
],
);
}
#[test]
fn test_element_string() {
let element = Element::String("First Item".to_string());
assert_de_tokens(&element, &[Token::Str("First Item")]);
}
pub struct ElementPreIter<'a> {
stack: Vec<&'a Element>,
}
impl<'a> Iterator for ElementPreIter<'a> {
type Item = &'a Element;
fn next(&mut self) -> Option<Self::Item> {
let node = self.stack.pop()?;
if let Element::Tag { element_list, .. } = node {
for child in element_list.iter().rev() {
self.stack.push(child);
}
}
Some(node)
}
}
pub struct PreOrderElement(Element);
impl From<Element> for PreOrderElement {
fn from(value: Element) -> PreOrderElement {
PreOrderElement(value)
}
}
impl<'a> IntoIterator for &'a PreOrderElement {
type Item = &'a Element;
type IntoIter = ElementPreIter<'a>;
fn into_iter(self) -> Self::IntoIter {
let PreOrderElement(element) = self;
ElementPreIter {
stack: vec![element],
}
}
}
pub struct ElementPostIter<'a> {
stack: Vec<&'a Element>,
result: Vec<&'a Element>,
}
impl<'a> Iterator for ElementPostIter<'a> {
type Item = &'a Element;
fn next(&mut self) -> Option<Self::Item> {
while let Some(node) = self.stack.pop() {
self.result.push(node);
if let Element::Tag { element_list, .. } = node {
for child in element_list.iter() {
self.stack.push(child);
}
}
}
self.result.pop()
}
}
pub struct PostOrderElement(Element);
impl From<Element> for PostOrderElement {
fn from(value: Element) -> PostOrderElement {
PostOrderElement(value)
}
}
impl<'a> IntoIterator for &'a PostOrderElement {
type Item = &'a Element;
type IntoIter = ElementPostIter<'a>;
fn into_iter(self) -> Self::IntoIter {
let PostOrderElement(element) = self;
ElementPostIter {
stack: vec![element],
result: vec![],
}
}
}
#[test]
fn test_iters() {
let text = Element::String("hello".to_string());
let span = Element::Tag {
name: "span".to_string(),
attributes: HashMap::new(),
element_list: vec![],
};
let tree = Element::Tag {
name: "div".to_string(),
attributes: HashMap::new(),
element_list: vec![text.clone(), span.clone()],
};
let nodes: Vec<_> = PreOrderElement::from(tree.clone())
.into_iter()
.map(|node| node.to_owned())
.collect();
assert_eq!(nodes, vec![tree.clone(), text.clone(), span.clone()]);
let nodes: Vec<_> = PostOrderElement::from(tree.clone())
.into_iter()
.map(|node| node.to_owned())
.collect();
assert_eq!(nodes, vec![text, span, tree]);
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Default)]
#[serde(untagged)]
pub enum AttributeValue {
String(String),
Number(f64),
Bool(bool),
#[default]
Null,
}
impl Display for AttributeValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AttributeValue::String(s) => write!(f, "{}", encode_unquoted_attribute(s)),
AttributeValue::Number(n) => write!(f, "{}", n),
AttributeValue::Bool(b) => write!(f, "{}", b),
AttributeValue::Null => write!(f, "null"),
}
}
}
#[test]
fn test_attribute_value_string() {
let value = AttributeValue::String("color:red".to_string());
assert_tokens(&value, &[Token::Str("color:red")]);
}
#[test]
fn test_attribute_value_number() {
let value = AttributeValue::Number(3.14);
assert_tokens(&value, &[Token::F64(3.14)]);
}
#[test]
fn test_attribute_value_number_keeps_double_precision() {
let value: AttributeValue = serde_json::from_str("16777217").unwrap();
assert_eq!(value, AttributeValue::Number(16777217.0));
assert_eq!(serde_json::to_string(&value).unwrap(), "16777217.0");
}
#[test]
fn test_attribute_value_bool() {
let value = AttributeValue::Bool(false);
assert_tokens(&value, &[Token::Bool(false)]);
}
#[test]
fn test_attribute_value_null() {
let value = AttributeValue::Null;
assert_tokens(&value, &[Token::Unit]);
}