Skip to main content

tiptap_rusty_parser/
node.rs

1//! Core data model: [`Node`] and [`Mark`].
2//!
3//! Mirrors Tiptap's `JSONContent` interface. Schema-agnostic: any node/mark
4//! `type` is accepted and unknown JSON fields are preserved in `extra` for
5//! faithful roundtrip.
6
7use serde::de::{self, Deserializer, MapAccess, Visitor};
8use serde::ser::{SerializeMap, Serializer};
9use serde::{Deserialize, Serialize};
10use serde_json::{Map, Value};
11use std::fmt;
12
13/// A single Tiptap/ProseMirror node.
14///
15/// All structural fields are optional to faithfully distinguish "missing" from
16/// "empty" on roundtrip. Unknown top-level keys land in [`Node::extra`].
17///
18/// `attrs` and `extra` are boxed so the common attr-less, extra-less node stays
19/// small (one pointer each); `extra: None` and `Some(empty)` compare equal and
20/// serialize identically.
21///
22/// Serde: serialized as a JSON object with keys in the order `type`, `attrs`,
23/// `content`, `marks`, `text` (each only when `Some`), then `extra` in its own
24/// order. On deserialize, a `null` known field reads as `None`, a repeated
25/// known field is a `duplicate field` error, and a repeated unknown key keeps
26/// its first position with the last value.
27#[derive(Debug, Clone, Default)]
28pub struct Node {
29    /// Node type, e.g. `"doc"`, `"paragraph"`, `"text"`.
30    pub node_type: Option<String>,
31
32    /// Node attributes.
33    pub attrs: Option<Box<Map<String, Value>>>,
34
35    /// Child nodes.
36    pub content: Option<Vec<Node>>,
37
38    /// Marks applied to this node (typically text nodes).
39    pub marks: Option<Vec<Mark>>,
40
41    /// Text payload (text nodes only).
42    pub text: Option<String>,
43
44    /// Any unknown/extra top-level fields, preserved verbatim (`None` = none).
45    pub extra: Option<Box<Map<String, Value>>>,
46}
47
48/// A mark applied to a node (e.g. `bold`, `italic`, `link`).
49///
50/// Serde: `type` (required, string), then `attrs` (when `Some`), then `extra`;
51/// same `null`/duplicate rules as [`Node`].
52#[derive(Debug, Clone)]
53pub struct Mark {
54    /// Mark type, e.g. `"bold"`.
55    pub mark_type: String,
56
57    /// Mark attributes (e.g. `href` for a link).
58    pub attrs: Option<Box<Map<String, Value>>>,
59
60    /// Any unknown/extra fields, preserved verbatim (`None` = none).
61    pub extra: Option<Box<Map<String, Value>>>,
62}
63
64impl Mark {
65    /// Create a mark of `mark_type` with no attrs.
66    ///
67    /// ```
68    /// use tiptap_rusty_parser::Mark;
69    /// let m = Mark::new("bold");
70    /// assert_eq!(m.mark_type, "bold");
71    /// ```
72    #[inline]
73    pub fn new(mark_type: impl Into<String>) -> Self {
74        Mark {
75            mark_type: mark_type.into(),
76            attrs: None,
77            extra: None,
78        }
79    }
80
81    /// Builder: set one attr.
82    #[inline]
83    pub fn attr(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
84        self.attrs
85            .get_or_insert_with(Default::default)
86            .insert(key.into(), value.into());
87        self
88    }
89
90    /// Attrs map, if any.
91    #[inline]
92    pub fn attrs(&self) -> Option<&Map<String, Value>> {
93        self.attrs.as_deref()
94    }
95
96    /// Mutable attrs map, creating it if absent.
97    #[inline]
98    pub fn attrs_mut(&mut self) -> &mut Map<String, Value> {
99        self.attrs.get_or_insert_with(Default::default)
100    }
101
102    /// Unknown/extra fields, if any (may be `Some(empty)`).
103    #[inline]
104    pub fn extra(&self) -> Option<&Map<String, Value>> {
105        self.extra.as_deref()
106    }
107
108    /// Mutable extra map, creating it if absent.
109    #[inline]
110    pub fn extra_mut(&mut self) -> &mut Map<String, Value> {
111        self.extra.get_or_insert_with(Default::default)
112    }
113}
114
115impl Node {
116    /// Attrs map, if any.
117    #[inline]
118    pub fn attrs(&self) -> Option<&Map<String, Value>> {
119        self.attrs.as_deref()
120    }
121
122    /// Unknown/extra fields, if any (may be `Some(empty)`).
123    #[inline]
124    pub fn extra(&self) -> Option<&Map<String, Value>> {
125        self.extra.as_deref()
126    }
127
128    /// Mutable extra map, creating it if absent.
129    #[inline]
130    pub fn extra_mut(&mut self) -> &mut Map<String, Value> {
131        self.extra.get_or_insert_with(Default::default)
132    }
133
134    /// Extra field `key`.
135    #[inline]
136    pub fn extra_field(&self, key: &str) -> Option<&Value> {
137        self.extra.as_ref()?.get(key)
138    }
139
140    /// Remove extra field `key`, dropping the map once empty.
141    pub fn remove_extra(&mut self, key: &str) -> Option<Value> {
142        let m = self.extra.as_mut()?;
143        let prev = m.remove(key);
144        if m.is_empty() {
145            self.extra = None;
146        }
147        prev
148    }
149}
150
151/// `None` and `Some(empty)` are the same extra set.
152#[inline]
153pub(crate) fn extra_eq(
154    a: &Option<Box<Map<String, Value>>>,
155    b: &Option<Box<Map<String, Value>>>,
156) -> bool {
157    match (a, b) {
158        (None, None) => true,
159        (Some(x), Some(y)) => x == y,
160        (Some(m), None) | (None, Some(m)) => m.is_empty(),
161    }
162}
163
164impl PartialEq for Node {
165    #[inline]
166    fn eq(&self, o: &Node) -> bool {
167        self.node_type == o.node_type
168            && self.text == o.text
169            && self.attrs == o.attrs
170            && self.marks == o.marks
171            && extra_eq(&self.extra, &o.extra)
172            && self.content == o.content
173    }
174}
175
176impl PartialEq for Mark {
177    #[inline]
178    fn eq(&self, o: &Mark) -> bool {
179        self.mark_type == o.mark_type && self.attrs == o.attrs && extra_eq(&self.extra, &o.extra)
180    }
181}
182
183// Hand-written serde: one pass over the map, known keys straight into typed
184// fields, the rest into `extra`. Avoids `#[serde(flatten)]`, which buffers
185// every unknown entry through serde's private `Content` tree.
186
187/// Object key: a known field (matched without allocating) or an unknown key.
188enum Key {
189    Type,
190    Attrs,
191    Content,
192    Marks,
193    Text,
194    Other(String),
195}
196
197impl Key {
198    #[inline]
199    fn known(s: &str) -> Option<Key> {
200        Some(match s {
201            "type" => Key::Type,
202            "attrs" => Key::Attrs,
203            "content" => Key::Content,
204            "marks" => Key::Marks,
205            "text" => Key::Text,
206            _ => return None,
207        })
208    }
209}
210
211struct KeyVisitor;
212
213impl<'de> Visitor<'de> for KeyVisitor {
214    type Value = Key;
215
216    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
217        f.write_str("field identifier")
218    }
219
220    #[inline]
221    fn visit_str<E: de::Error>(self, v: &str) -> Result<Key, E> {
222        Ok(Key::known(v).unwrap_or_else(|| Key::Other(v.to_owned())))
223    }
224
225    #[inline]
226    fn visit_string<E: de::Error>(self, v: String) -> Result<Key, E> {
227        Ok(Key::known(&v).unwrap_or(Key::Other(v)))
228    }
229
230    fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Key, E> {
231        match std::str::from_utf8(v) {
232            Ok(s) => self.visit_str(s),
233            Err(_) => Err(E::invalid_value(de::Unexpected::Bytes(v), &self)),
234        }
235    }
236}
237
238impl<'de> Deserialize<'de> for Key {
239    #[inline]
240    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Key, D::Error> {
241        d.deserialize_identifier(KeyVisitor)
242    }
243}
244
245/// Error on a repeated known field (matches the derive's behaviour).
246#[inline]
247fn once<E: de::Error>(seen: &mut u8, bit: u8, name: &'static str) -> Result<(), E> {
248    if *seen & bit != 0 {
249        return Err(E::duplicate_field(name));
250    }
251    *seen |= bit;
252    Ok(())
253}
254
255impl<'de> Deserialize<'de> for Node {
256    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Node, D::Error> {
257        struct V;
258        impl<'de> Visitor<'de> for V {
259            type Value = Node;
260
261            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
262                f.write_str("struct Node")
263            }
264
265            fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Node, A::Error> {
266                let mut n = Node::default();
267                let mut seen = 0u8;
268                while let Some(key) = map.next_key::<Key>()? {
269                    match key {
270                        Key::Type => {
271                            once(&mut seen, 1, "type")?;
272                            n.node_type = map.next_value()?;
273                        }
274                        Key::Attrs => {
275                            once(&mut seen, 2, "attrs")?;
276                            n.attrs = map.next_value()?;
277                        }
278                        Key::Content => {
279                            once(&mut seen, 4, "content")?;
280                            n.content = map.next_value()?;
281                        }
282                        Key::Marks => {
283                            once(&mut seen, 8, "marks")?;
284                            n.marks = map.next_value()?;
285                        }
286                        Key::Text => {
287                            once(&mut seen, 16, "text")?;
288                            n.text = map.next_value()?;
289                        }
290                        Key::Other(k) => {
291                            let v: Value = map.next_value()?;
292                            n.extra_mut().insert(k, v);
293                        }
294                    }
295                }
296                Ok(n)
297            }
298        }
299        d.deserialize_map(V)
300    }
301}
302
303impl Serialize for Node {
304    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
305        let len = self.node_type.is_some() as usize
306            + self.attrs.is_some() as usize
307            + self.content.is_some() as usize
308            + self.marks.is_some() as usize
309            + self.text.is_some() as usize
310            + self.extra.as_ref().map_or(0, |e| e.len());
311        let mut m = s.serialize_map(Some(len))?;
312        if let Some(t) = &self.node_type {
313            m.serialize_entry("type", t)?;
314        }
315        if let Some(a) = &self.attrs {
316            m.serialize_entry("attrs", a)?;
317        }
318        if let Some(c) = &self.content {
319            m.serialize_entry("content", c)?;
320        }
321        if let Some(mk) = &self.marks {
322            m.serialize_entry("marks", mk)?;
323        }
324        if let Some(t) = &self.text {
325            m.serialize_entry("text", t)?;
326        }
327        for (k, v) in self.extra.iter().flat_map(|e| e.iter()) {
328            m.serialize_entry(k, v)?;
329        }
330        m.end()
331    }
332}
333
334impl<'de> Deserialize<'de> for Mark {
335    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Mark, D::Error> {
336        struct V;
337        impl<'de> Visitor<'de> for V {
338            type Value = Mark;
339
340            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
341                f.write_str("struct Mark")
342            }
343
344            fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Mark, A::Error> {
345                let mut mark_type: Option<String> = None;
346                let mut attrs = None;
347                let mut extra: Option<Box<Map<String, Value>>> = None;
348                let mut seen = 0u8;
349                while let Some(key) = map.next_key::<Key>()? {
350                    match key {
351                        Key::Type => {
352                            once(&mut seen, 1, "type")?;
353                            mark_type = Some(map.next_value()?);
354                        }
355                        Key::Attrs => {
356                            once(&mut seen, 2, "attrs")?;
357                            attrs = map.next_value()?;
358                        }
359                        Key::Content => extra_entry(&mut map, &mut extra, "content")?,
360                        Key::Marks => extra_entry(&mut map, &mut extra, "marks")?,
361                        Key::Text => extra_entry(&mut map, &mut extra, "text")?,
362                        Key::Other(k) => {
363                            let v: Value = map.next_value()?;
364                            extra.get_or_insert_with(Default::default).insert(k, v);
365                        }
366                    }
367                }
368                let mark_type = mark_type.ok_or_else(|| de::Error::missing_field("type"))?;
369                Ok(Mark {
370                    mark_type,
371                    attrs,
372                    extra,
373                })
374            }
375        }
376        d.deserialize_map(V)
377    }
378}
379
380/// `Mark` has no `content`/`marks`/`text` fields: those keys are unknown there.
381#[inline]
382fn extra_entry<'de, A: MapAccess<'de>>(
383    map: &mut A,
384    extra: &mut Option<Box<Map<String, Value>>>,
385    key: &str,
386) -> Result<(), A::Error> {
387    let v: Value = map.next_value()?;
388    extra
389        .get_or_insert_with(Default::default)
390        .insert(key.to_owned(), v);
391    Ok(())
392}
393
394impl Serialize for Mark {
395    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
396        let len = 1 + self.attrs.is_some() as usize + self.extra.as_ref().map_or(0, |e| e.len());
397        let mut m = s.serialize_map(Some(len))?;
398        m.serialize_entry("type", &self.mark_type)?;
399        if let Some(a) = &self.attrs {
400            m.serialize_entry("attrs", a)?;
401        }
402        for (k, v) in self.extra.iter().flat_map(|e| e.iter()) {
403            m.serialize_entry(k, v)?;
404        }
405        m.end()
406    }
407}