1use super::node::Node;
2use super::options::FormattingOptions;
3use super::span::SourceSpan;
4use serde::{Deserialize, Serialize, Serializer};
5use std::borrow::Cow;
6use std::collections::{BTreeMap, HashMap};
7use std::default::Default;
8use std::fmt::Display;
9use std::result::Result;
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
13#[serde(rename_all = "camelCase")]
14pub enum ElementVariant {
16 Normal,
18 Void,
20}
21
22pub type Attributes<'s> = HashMap<Cow<'s, str>, Option<Cow<'s, str>>>;
23
24#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
26#[serde(rename_all = "camelCase")]
27pub struct Element<'s> {
28 pub name: Cow<'s, str>,
30
31 pub variant: ElementVariant,
33
34 #[serde(skip_serializing_if = "HashMap::is_empty")]
36 #[serde(serialize_with = "ordered_map")]
37 #[serde(default)]
38 pub attributes: Attributes<'s>,
39
40 #[serde(skip_serializing_if = "Vec::is_empty")]
42 #[serde(default)]
43 pub classes: Vec<Cow<'s, str>>,
44
45 #[serde(default, borrow, skip_serializing_if = "Vec::is_empty")]
47 pub children: Vec<Node<'s>>,
48
49 #[serde(skip)]
51 #[serde(default)]
52 pub source_span: SourceSpan<'s>,
53}
54
55impl<'s> Element<'s> {
56 pub fn fmt_opt<W>(&self, f: &mut W, o: &FormattingOptions, depth: usize) -> std::fmt::Result
57 where
58 W: std::fmt::Write,
59 {
60 o.fmt_depth(f, depth)?;
62
63 write!(f, "<{}", self.name)?;
65
66 let attr_len: usize = self
68 .attributes
69 .iter()
70 .map(|(k, v)| k.len() + v.as_ref().map(|v| v.len()).unwrap_or(0) + 4)
71 .sum();
72
73 let classes_len = if self.classes.is_empty() {
75 0
76 } else {
77 self.classes.iter().map(|c| c.len() + 1).sum::<usize>() + 8
78 };
79
80 let e_len = depth + 1 + self.name.len() + attr_len + 1 + classes_len;
82
83 let c_inline = if e_len > o.max_len && o.new_lines {
85 let mut c_inline = String::new();
86 c_inline.push('\n');
87 o.fmt_depth(&mut c_inline, depth + o.tab_size as usize)?;
88 c_inline
89 } else {
90 String::from(" ")
91 };
92
93 if !self.classes.is_empty() {
95 let classes = self
96 .classes
97 .iter()
98 .enumerate()
99 .map(|(i, c)| {
100 let c = c.trim();
101 if c.is_empty() {
102 String::new()
103 } else if i == 0 {
104 c.to_string()
105 } else {
106 format!(" {c}")
107 }
108 })
109 .collect::<String>();
110 write!(f, "{0}class={1}{classes}{1}", c_inline, o.quotes())?
111 }
112
113 let ordered_attributes: BTreeMap<_, _> = self.attributes.iter().collect();
115 for (k, v) in ordered_attributes {
116 match v {
117 Some(v) => {
118 let v = match o.double_quot {
119 true => v.replace('\"', "\\\""),
120 false => v.replace('\'', "\\\'"),
121 };
122 write!(f, "{0}{k}={1}{v}{1}", c_inline, o.quotes())?
123 }
124 None => write!(f, "{0}{k}", c_inline)?,
125 }
126 }
127
128 match (
130 e_len > o.max_len,
131 self.variant == ElementVariant::Normal && !self.children.is_empty(),
132 ) {
133 (true, true) => {
134 write!(f, "\n")?;
135 o.fmt_depth(f, depth)?;
136 write!(f, ">")?
137 }
138 (true, false) => {
139 write!(f, "\n")?;
140 o.fmt_depth(f, depth)?;
141 write!(f, "/>")?;
142 return Ok(());
143 }
144 (false, true) => write!(f, ">")?,
145 (false, false) => {
146 write!(f, "/>")?;
147 return Ok(());
148 }
149 }
150
151 if let Some(text) = self.children.get(0).and_then(|c| c.text()) {
153 if self.children.len() == 1
154 && depth + o.tab_size as usize + text.len() + self.name.len() + 3 <= o.max_len
155 {
156 write!(f, "{}", text)?;
157 write!(f, "</{0}>", self.name)?;
158 return Ok(());
159 }
160 }
161
162 for child in self.children.iter() {
164 write!(f, "\n")?;
165 child.fmt_opt(f, o, depth + o.tab_size as usize)?;
166 }
167 write!(f, "\n")?;
168 o.fmt_depth(f, depth)?;
169 write!(f, "</{0}>", self.name)?;
170
171 Ok(())
172 }
173}
174
175impl<'s> Display for Element<'s> {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 self.fmt_opt(f, &FormattingOptions::pretty(), 0)
178 }
179}
180
181impl<'s> Default for Element<'s> {
182 fn default() -> Self {
183 Self {
184 name: Cow::Borrowed(""),
185 variant: ElementVariant::Void,
186 classes: vec![],
187 attributes: HashMap::new(),
188 children: vec![],
189 source_span: SourceSpan::default(),
190 }
191 }
192}
193
194fn ordered_map<S: Serializer>(value: &Attributes, serializer: S) -> Result<S::Ok, S::Error> {
195 let ordered: BTreeMap<_, _> = value.iter().collect();
196 ordered.serialize(serializer)
197}