acdc_parser/model/
attributes.rs

1use rustc_hash::FxHashMap;
2use serde::{
3    Deserialize, Serialize,
4    de::Deserializer,
5    ser::{SerializeMap, Serializer},
6};
7
8/// Internal shared implementation for both document and element attributes.
9///
10/// This type is not exported directly. Use `DocumentAttributes` for document-level
11/// attributes or `ElementAttributes` for element-level attributes.
12#[derive(Debug, PartialEq, Clone)]
13struct AttributeMap {
14    /// All attributes including defaults
15    all: FxHashMap<AttributeName, AttributeValue>,
16    /// Only explicitly set attributes (not defaults) - used for serialization
17    explicit: FxHashMap<AttributeName, AttributeValue>,
18}
19
20impl Default for AttributeMap {
21    fn default() -> Self {
22        AttributeMap {
23            all: crate::constants::default_attributes(),
24            explicit: FxHashMap::default(), // Defaults are not explicit
25        }
26    }
27}
28
29impl AttributeMap {
30    fn empty() -> Self {
31        AttributeMap {
32            all: FxHashMap::default(),
33            explicit: FxHashMap::default(),
34        }
35    }
36
37    fn iter(&self) -> impl Iterator<Item = (&AttributeName, &AttributeValue)> {
38        self.all.iter()
39    }
40
41    fn is_empty(&self) -> bool {
42        // We only consider explicit attributes for emptiness because defaults are always
43        // present.
44        self.explicit.is_empty()
45    }
46
47    fn insert(&mut self, name: AttributeName, value: AttributeValue) {
48        if !self.contains_key(&name) {
49            self.all.insert(name.clone(), value.clone());
50            self.explicit.insert(name, value); // Track as explicit
51        }
52    }
53
54    fn set(&mut self, name: AttributeName, value: AttributeValue) {
55        self.all.insert(name.clone(), value.clone());
56        self.explicit.insert(name, value); // Track as explicit
57    }
58
59    fn get(&self, name: &str) -> Option<&AttributeValue> {
60        self.all.get(name)
61    }
62
63    fn contains_key(&self, name: &str) -> bool {
64        self.all.contains_key(name)
65    }
66
67    fn remove(&mut self, name: &str) -> Option<AttributeValue> {
68        self.explicit.remove(name);
69        self.all.remove(name)
70    }
71
72    fn merge(&mut self, other: AttributeMap) {
73        for (key, value) in other.all {
74            self.insert(key, value);
75        }
76    }
77}
78
79impl Serialize for AttributeMap {
80    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
81    where
82        S: Serializer,
83    {
84        // Only serialize explicitly set attributes, not defaults
85        let mut sorted_keys: Vec<_> = self.explicit.keys().collect();
86        sorted_keys.sort();
87
88        let mut state = serializer.serialize_map(Some(self.explicit.len()))?;
89        for key in sorted_keys {
90            if let Some(value) = &self.explicit.get(key) {
91                match value {
92                    AttributeValue::Bool(true) => {
93                        if key == "toc" {
94                            state.serialize_entry(key, "")?;
95                        } else {
96                            state.serialize_entry(key, &true)?;
97                        }
98                    }
99                    value @ (AttributeValue::Bool(false)
100                    | AttributeValue::String(_)
101                    | AttributeValue::None) => {
102                        state.serialize_entry(key, value)?;
103                    }
104                }
105            }
106        }
107        state.end()
108    }
109}
110
111impl<'de> Deserialize<'de> for AttributeMap {
112    fn deserialize<D>(deserializer: D) -> Result<AttributeMap, D::Error>
113    where
114        D: Deserializer<'de>,
115    {
116        let explicit = FxHashMap::deserialize(deserializer).unwrap_or_default();
117        // When deserializing, explicit attributes are the only ones we have
118        // Defaults will be added by DocumentAttributes::deserialize
119        Ok(AttributeMap {
120            all: explicit.clone(),
121            explicit,
122        })
123    }
124}
125
126/// Document-level attributes with universal defaults.
127///
128/// These attributes apply to the entire document and include defaults for
129/// admonition captions, TOC settings, structural settings, etc.
130///
131/// Use `DocumentAttributes::default()` to get a map with universal defaults applied.
132#[derive(Debug, PartialEq, Clone, Default)]
133pub struct DocumentAttributes(AttributeMap);
134
135impl DocumentAttributes {
136    /// Iterate over all attributes.
137    pub fn iter(&self) -> impl Iterator<Item = (&AttributeName, &AttributeValue)> {
138        self.0.iter()
139    }
140
141    /// Check if the attribute map is empty.
142    #[must_use]
143    pub fn is_empty(&self) -> bool {
144        self.0.is_empty()
145    }
146
147    /// Insert a new attribute.
148    ///
149    /// NOTE: This will *NOT* overwrite an existing attribute with the same name.
150    pub fn insert(&mut self, name: AttributeName, value: AttributeValue) {
151        self.0.insert(name, value);
152    }
153
154    /// Set an attribute, overwriting any existing value.
155    pub fn set(&mut self, name: AttributeName, value: AttributeValue) {
156        self.0.set(name, value);
157    }
158
159    /// Get an attribute value by name.
160    #[must_use]
161    pub fn get(&self, name: &str) -> Option<&AttributeValue> {
162        self.0.get(name)
163    }
164
165    /// Check if an attribute exists.
166    #[must_use]
167    pub fn contains_key(&self, name: &str) -> bool {
168        self.0.contains_key(name)
169    }
170
171    /// Remove an attribute by name.
172    pub fn remove(&mut self, name: &str) -> Option<AttributeValue> {
173        self.0.remove(name)
174    }
175
176    /// Merge another attribute map into this one.
177    pub fn merge(&mut self, other: Self) {
178        self.0.merge(other.0);
179    }
180
181    /// Helper to get a string value.
182    ///
183    /// Strips surrounding quotes from the value if present (parser quirk workaround).
184    #[must_use]
185    pub fn get_string(&self, name: &str) -> Option<String> {
186        self.get(name).and_then(|v| match v {
187            AttributeValue::String(s) => {
188                // Strip surrounding quotes if present (parser includes them for quoted values)
189                let trimmed = s.trim_matches('"');
190                Some(trimmed.to_string())
191            }
192            AttributeValue::None | AttributeValue::Bool(_) => None,
193        })
194    }
195}
196
197impl Serialize for DocumentAttributes {
198    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
199    where
200        S: Serializer,
201    {
202        self.0.serialize(serializer)
203    }
204}
205
206impl<'de> Deserialize<'de> for DocumentAttributes {
207    fn deserialize<D>(deserializer: D) -> Result<DocumentAttributes, D::Error>
208    where
209        D: Deserializer<'de>,
210    {
211        let mut map = AttributeMap::deserialize(deserializer)?;
212
213        // Re-apply defaults after deserialization
214        // This ensures defaults are available at runtime even though they weren't serialized
215        for (name, value) in crate::constants::default_attributes() {
216            map.all
217                .entry(name)
218                .and_modify(|v| *v = value.clone())
219                .or_insert(value.clone());
220        }
221
222        Ok(DocumentAttributes(map))
223    }
224}
225
226/// Element-level attributes (for blocks, sections, etc.).
227///
228/// These attributes are specific to individual elements and start empty.
229///
230/// Use `ElementAttributes::default()` to get an empty attribute map.
231#[derive(Debug, PartialEq, Clone)]
232pub struct ElementAttributes(AttributeMap);
233
234impl Default for ElementAttributes {
235    fn default() -> Self {
236        ElementAttributes(AttributeMap::empty())
237    }
238}
239
240impl ElementAttributes {
241    /// Iterate over all attributes.
242    pub fn iter(&self) -> impl Iterator<Item = (&AttributeName, &AttributeValue)> {
243        self.0.iter()
244    }
245
246    /// Check if the attribute map is empty.
247    #[must_use]
248    pub fn is_empty(&self) -> bool {
249        self.0.is_empty()
250    }
251
252    /// Insert a new attribute.
253    ///
254    /// NOTE: This will *NOT* overwrite an existing attribute with the same name.
255    pub fn insert(&mut self, name: AttributeName, value: AttributeValue) {
256        self.0.insert(name, value);
257    }
258
259    /// Set an attribute, overwriting any existing value.
260    pub fn set(&mut self, name: AttributeName, value: AttributeValue) {
261        self.0.set(name, value);
262    }
263
264    /// Get an attribute value by name.
265    #[must_use]
266    pub fn get(&self, name: &str) -> Option<&AttributeValue> {
267        self.0.get(name)
268    }
269
270    /// Check if an attribute exists.
271    #[must_use]
272    pub fn contains_key(&self, name: &str) -> bool {
273        self.0.contains_key(name)
274    }
275
276    /// Remove an attribute by name.
277    pub fn remove(&mut self, name: &str) -> Option<AttributeValue> {
278        self.0.remove(name)
279    }
280
281    /// Merge another attribute map into this one.
282    pub fn merge(&mut self, other: Self) {
283        self.0.merge(other.0);
284    }
285
286    /// Helper to get a string value.
287    ///
288    /// Strips surrounding quotes from the value if present (parser quirk workaround).
289    #[must_use]
290    pub fn get_string(&self, name: &str) -> Option<String> {
291        self.get(name).and_then(|v| match v {
292            AttributeValue::String(s) => {
293                // Strip surrounding quotes if present (parser includes them for quoted values)
294                let trimmed = s.trim_matches('"');
295                Some(trimmed.to_string())
296            }
297            AttributeValue::None | AttributeValue::Bool(_) => None,
298        })
299    }
300}
301
302impl Serialize for ElementAttributes {
303    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
304    where
305        S: Serializer,
306    {
307        self.0.serialize(serializer)
308    }
309}
310
311impl<'de> Deserialize<'de> for ElementAttributes {
312    fn deserialize<D>(deserializer: D) -> Result<ElementAttributes, D::Error>
313    where
314        D: Deserializer<'de>,
315    {
316        AttributeMap::deserialize(deserializer).map(ElementAttributes)
317    }
318}
319
320/// An `AttributeName` represents the name of an attribute in a document.
321pub type AttributeName = String;
322
323/// An `AttributeValue` represents the value of an attribute in a document.
324///
325/// An attribute value can be a string, a boolean, or nothing
326#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
327#[serde(untagged)]
328#[non_exhaustive]
329pub enum AttributeValue {
330    /// A string attribute value.
331    String(String),
332    /// A boolean attribute value. `false` means it is unset.
333    Bool(bool),
334    /// No value (or it was unset)
335    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}