1use rustc_hash::FxHashMap;
2use serde::{
3 Serialize,
4 ser::{SerializeMap, Serializer},
5};
6
7pub const MAX_TOC_LEVELS: u8 = 5;
8pub const MAX_SECTION_LEVELS: u8 = 5;
9
10#[must_use]
15pub fn strip_quotes(s: &str) -> &str {
16 s.trim_start_matches(['"', '\''])
17 .trim_end_matches(['"', '\''])
18}
19
20#[derive(Debug, PartialEq, Clone)]
25struct AttributeMap {
26 all: FxHashMap<AttributeName, AttributeValue>,
28 explicit: FxHashMap<AttributeName, AttributeValue>,
30}
31
32impl Default for AttributeMap {
33 fn default() -> Self {
34 AttributeMap {
35 all: crate::constants::default_attributes(),
36 explicit: FxHashMap::default(), }
38 }
39}
40
41impl AttributeMap {
42 fn empty() -> Self {
43 AttributeMap {
44 all: FxHashMap::default(),
45 explicit: FxHashMap::default(),
46 }
47 }
48
49 fn iter(&self) -> impl Iterator<Item = (&AttributeName, &AttributeValue)> {
50 self.all.iter()
51 }
52
53 fn is_empty(&self) -> bool {
54 self.explicit.is_empty()
57 }
58
59 fn insert(&mut self, name: AttributeName, value: AttributeValue) {
60 if !self.contains_key(&name) {
61 self.all.insert(name.clone(), value.clone());
62 self.explicit.insert(name, value); }
64 }
65
66 fn set(&mut self, name: AttributeName, value: AttributeValue) {
67 self.all.insert(name.clone(), value.clone());
68 self.explicit.insert(name, value); }
70
71 fn get(&self, name: &str) -> Option<&AttributeValue> {
72 self.all.get(name)
73 }
74
75 fn contains_key(&self, name: &str) -> bool {
76 self.all.contains_key(name)
77 }
78
79 fn remove(&mut self, name: &str) -> Option<AttributeValue> {
80 self.explicit.remove(name);
81 self.all.remove(name)
82 }
83
84 fn merge(&mut self, other: AttributeMap) {
85 for (key, value) in other.all {
86 self.insert(key, value);
87 }
88 }
89}
90
91impl Serialize for AttributeMap {
92 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
93 where
94 S: Serializer,
95 {
96 let mut sorted_keys: Vec<_> = self.explicit.keys().collect();
98 sorted_keys.sort();
99
100 let mut state = serializer.serialize_map(Some(self.explicit.len()))?;
101 for key in sorted_keys {
102 if let Some(value) = &self.explicit.get(key) {
103 match value {
104 AttributeValue::Bool(true) => {
105 if key == "toc" {
106 state.serialize_entry(key, "")?;
107 } else {
108 state.serialize_entry(key, &true)?;
109 }
110 }
111 value @ (AttributeValue::Bool(false)
112 | AttributeValue::String(_)
113 | AttributeValue::None) => {
114 state.serialize_entry(key, value)?;
115 }
116 }
117 }
118 }
119 state.end()
120 }
121}
122
123fn validate_bounded_attribute(key: &str, value: &AttributeValue) {
128 let AttributeValue::String(s) = value else {
129 return;
130 };
131
132 match key {
133 "sectnumlevels" => {
134 if let Ok(level) = s.parse::<u8>()
135 && level > MAX_SECTION_LEVELS
136 {
137 tracing::warn!(
138 attribute = "sectnumlevels",
139 value = level,
140 "sectnumlevels must be between 0 and {MAX_SECTION_LEVELS}, got {level}. \
141 Values above {MAX_SECTION_LEVELS} will be treated as {MAX_SECTION_LEVELS}."
142 );
143 }
144 }
145 "toclevels" => {
146 if let Ok(level) = s.parse::<u8>()
147 && level > MAX_TOC_LEVELS
148 {
149 tracing::warn!(
150 attribute = "toclevels",
151 value = level,
152 "toclevels must be between 0 and {MAX_TOC_LEVELS}, got {level}. \
153 Values above {MAX_TOC_LEVELS} will be treated as {MAX_TOC_LEVELS}."
154 );
155 }
156 }
157 _ => {}
158 }
159}
160
161#[derive(Debug, PartialEq, Clone, Default)]
168pub struct DocumentAttributes(AttributeMap);
169
170impl DocumentAttributes {
171 pub fn iter(&self) -> impl Iterator<Item = (&AttributeName, &AttributeValue)> {
173 self.0.iter()
174 }
175
176 #[must_use]
178 pub fn is_empty(&self) -> bool {
179 self.0.is_empty()
180 }
181
182 pub fn insert(&mut self, name: AttributeName, value: AttributeValue) {
186 validate_bounded_attribute(&name, &value);
187 self.0.insert(name, value);
188 }
189
190 pub fn set(&mut self, name: AttributeName, value: AttributeValue) {
192 validate_bounded_attribute(&name, &value);
193 self.0.set(name, value);
194 }
195
196 #[must_use]
198 pub fn get(&self, name: &str) -> Option<&AttributeValue> {
199 self.0.get(name)
200 }
201
202 #[must_use]
204 pub fn contains_key(&self, name: &str) -> bool {
205 self.0.contains_key(name)
206 }
207
208 pub fn remove(&mut self, name: &str) -> Option<AttributeValue> {
210 self.0.remove(name)
211 }
212
213 pub fn merge(&mut self, other: Self) {
215 self.0.merge(other.0);
216 }
217
218 #[must_use]
222 pub fn get_string(&self, name: &str) -> Option<String> {
223 self.get(name).and_then(|v| match v {
224 AttributeValue::String(s) => Some(strip_quotes(s).to_string()),
225 AttributeValue::None | AttributeValue::Bool(_) => None,
226 })
227 }
228}
229
230impl Serialize for DocumentAttributes {
231 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
232 where
233 S: Serializer,
234 {
235 self.0.serialize(serializer)
236 }
237}
238
239#[derive(Debug, PartialEq, Clone)]
245pub struct ElementAttributes(AttributeMap);
246
247impl Default for ElementAttributes {
248 fn default() -> Self {
249 ElementAttributes(AttributeMap::empty())
250 }
251}
252
253impl ElementAttributes {
254 pub fn iter(&self) -> impl Iterator<Item = (&AttributeName, &AttributeValue)> {
256 self.0.iter()
257 }
258
259 #[must_use]
261 pub fn is_empty(&self) -> bool {
262 self.0.is_empty()
263 }
264
265 pub fn insert(&mut self, name: AttributeName, value: AttributeValue) {
269 self.0.insert(name, value);
270 }
271
272 pub fn set(&mut self, name: AttributeName, value: AttributeValue) {
274 self.0.set(name, value);
275 }
276
277 #[must_use]
279 pub fn get(&self, name: &str) -> Option<&AttributeValue> {
280 self.0.get(name)
281 }
282
283 #[must_use]
285 pub fn contains_key(&self, name: &str) -> bool {
286 self.0.contains_key(name)
287 }
288
289 pub fn remove(&mut self, name: &str) -> Option<AttributeValue> {
291 self.0.remove(name)
292 }
293
294 pub fn merge(&mut self, other: Self) {
296 self.0.merge(other.0);
297 }
298
299 #[must_use]
303 pub fn get_string(&self, name: &str) -> Option<String> {
304 self.get(name).and_then(|v| match v {
305 AttributeValue::String(s) => Some(strip_quotes(s).to_string()),
306 AttributeValue::None | AttributeValue::Bool(_) => None,
307 })
308 }
309}
310
311impl Serialize for ElementAttributes {
312 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
313 where
314 S: Serializer,
315 {
316 self.0.serialize(serializer)
317 }
318}
319
320pub type AttributeName = String;
322
323#[derive(Clone, Debug, PartialEq, Serialize)]
327#[serde(untagged)]
328#[non_exhaustive]
329pub enum AttributeValue {
330 String(String),
332 Bool(bool),
334 None,
336}
337
338impl std::fmt::Display for AttributeValue {
339 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
340 match self {
341 AttributeValue::String(value) => write!(f, "{value}"),
342 AttributeValue::Bool(value) => write!(f, "{value}"),
343 AttributeValue::None => write!(f, "null"),
344 }
345 }
346}
347
348impl From<&str> for AttributeValue {
349 fn from(value: &str) -> Self {
350 AttributeValue::String(value.to_string())
351 }
352}
353
354impl From<String> for AttributeValue {
355 fn from(value: String) -> Self {
356 AttributeValue::String(value)
357 }
358}
359
360impl From<bool> for AttributeValue {
361 fn from(value: bool) -> Self {
362 AttributeValue::Bool(value)
363 }
364}
365
366impl From<()> for AttributeValue {
367 fn from((): ()) -> Self {
368 AttributeValue::None
369 }
370}