Skip to main content

socketry_markdown/
mdast.rs

1// Released under the MIT License.
2// Copyright, 2022, by Bernhard Berger.
3// Copyright, 2022-2025, by Titus Wormer.
4// Copyright, 2023, by Kyle McCarthy.
5// Copyright, 2023, by Mia.
6// Copyright, 2023, by Rafael Bachmann.
7// Copyright, 2024, by Harsha Teja Kanna.
8// Copyright, 2026, by Samuel Williams.
9
10//! markdown syntax tree: [mdast][].
11//!
12//! [mdast]: https://github.com/syntax-tree/mdast
13use crate::unist::Position;
14use alloc::{
15    fmt,
16    string::{String, ToString},
17    vec::Vec,
18};
19
20mod headings;
21pub use headings::{HeadingEntry, HeadingOptions, Headings};
22mod frontmatter;
23pub use frontmatter::Frontmatter;
24
25/// MDX: relative byte index into a string, to an absolute byte index into the
26/// whole document.
27pub type Stop = (usize, usize);
28
29/// Explicitness of a reference.
30#[derive(Clone, Copy, Debug, Eq, PartialEq)]
31#[cfg_attr(
32    feature = "serde",
33    derive(serde::Serialize, serde::Deserialize),
34    serde(rename_all = "lowercase")
35)]
36pub enum ReferenceKind {
37    /// The reference is implicit, its identifier inferred from its content.
38    Shortcut,
39    /// The reference is explicit, its identifier inferred from its content.
40    Collapsed,
41    /// The reference is explicit, its identifier explicitly set.
42    Full,
43}
44
45/// GFM: alignment of phrasing content.
46///
47/// Used to align the contents of table cells within a table.
48#[derive(Clone, Copy, Debug, Eq, PartialEq)]
49pub enum AlignKind {
50    /// Left alignment.
51    ///
52    /// See the `left` value of the `text-align` CSS property.
53    ///
54    /// ```markdown
55    ///   | | aaa |
56    /// > | | :-- |
57    ///       ^^^
58    /// ```
59    Left,
60    /// Right alignment.
61    ///
62    /// See the `right` value of the `text-align` CSS property.
63    ///
64    /// ```markdown
65    ///   | | aaa |
66    /// > | | --: |
67    ///       ^^^
68    /// ```
69    Right,
70    /// Center alignment.
71    ///
72    /// See the `center` value of the `text-align` CSS property.
73    ///
74    /// ```markdown
75    ///   | | aaa |
76    /// > | | :-: |
77    ///       ^^^
78    /// ```
79    Center,
80    /// No alignment.
81    ///
82    /// Phrasing content is aligned as defined by the host environment.
83    ///
84    /// ```markdown
85    ///   | | aaa |
86    /// > | | --- |
87    ///       ^^^
88    /// ```
89    None,
90}
91
92/// Implement serde according to <https://github.com/syntax-tree/mdast#aligntype>
93#[cfg(feature = "serde")]
94impl serde::ser::Serialize for AlignKind {
95    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
96    where
97        S: serde::ser::Serializer,
98    {
99        match self {
100            AlignKind::Left => serializer.serialize_unit_variant("AlignKind", 0, "left"),
101            AlignKind::Right => serializer.serialize_unit_variant("AlignKind", 1, "right"),
102            AlignKind::Center => serializer.serialize_unit_variant("AlignKind", 2, "center"),
103            AlignKind::None => serializer.serialize_none(),
104        }
105    }
106}
107
108#[cfg(feature = "serde")]
109struct AlignKindVisitor;
110
111#[cfg(feature = "serde")]
112impl serde::de::Visitor<'_> for AlignKindVisitor {
113    type Value = AlignKind;
114
115    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
116        formatter.write_str("'left', 'right', 'center' or null")
117    }
118
119    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
120    where
121        E: serde::de::Error,
122    {
123        match v {
124            "left" => Ok(AlignKind::Left),
125            "right" => Ok(AlignKind::Right),
126            "center" => Ok(AlignKind::Center),
127            &_ => Err(serde::de::Error::invalid_type(
128                serde::de::Unexpected::Str(v),
129                &self,
130            )),
131        }
132    }
133
134    fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
135    where
136        E: serde::de::Error,
137    {
138        match v {
139            b"left" => Ok(AlignKind::Left),
140            b"right" => Ok(AlignKind::Right),
141            b"center" => Ok(AlignKind::Center),
142            &_ => Err(serde::de::Error::invalid_type(
143                serde::de::Unexpected::Bytes(v),
144                &self,
145            )),
146        }
147    }
148
149    fn visit_none<E>(self) -> Result<Self::Value, E>
150    where
151        E: serde::de::Error,
152    {
153        Ok(AlignKind::None)
154    }
155
156    fn visit_unit<E>(self) -> Result<Self::Value, E>
157    where
158        E: serde::de::Error,
159    {
160        Ok(AlignKind::None)
161    }
162}
163
164#[cfg(feature = "serde")]
165impl<'de> serde::de::Deserialize<'de> for AlignKind {
166    fn deserialize<D>(deserializer: D) -> Result<AlignKind, D::Error>
167    where
168        D: serde::de::Deserializer<'de>,
169    {
170        deserializer.deserialize_any(AlignKindVisitor)
171    }
172}
173
174/// Nodes.
175#[derive(Clone, Eq, PartialEq)]
176#[cfg_attr(
177    feature = "serde",
178    derive(serde::Serialize, serde::Deserialize),
179    serde(tag = "type", rename_all = "camelCase")
180)]
181pub enum Node {
182    // Document:
183    /// Root.
184    Root(Root),
185
186    // Container:
187    /// A transparent fragment containing a sequence of nodes.
188    ///
189    /// Fragments are useful for holding children extracted from another node.
190    Fragment(Fragment),
191    /// Block quote.
192    Blockquote(Blockquote),
193    /// Footnote definition.
194    FootnoteDefinition(FootnoteDefinition),
195    /// MDX: JSX element (container).
196    MdxJsxFlowElement(MdxJsxFlowElement),
197    /// List.
198    List(List),
199
200    // Frontmatter:
201    /// Language-agnostic frontmatter with an explicit format hint.
202    Frontmatter(Frontmatter),
203    /// MDX.js ESM.
204    MdxjsEsm(MdxjsEsm),
205    /// Toml.
206    Toml(Toml),
207    /// Yaml.
208    Yaml(Yaml),
209
210    // Phrasing:
211    /// Break.
212    Break(Break),
213    /// Code (phrasing).
214    InlineCode(InlineCode),
215    /// Math (phrasing).
216    InlineMath(InlineMath),
217    /// Delete.
218    Delete(Delete),
219    /// Emphasis.
220    Emphasis(Emphasis),
221    // MDX: expression (text).
222    MdxTextExpression(MdxTextExpression),
223    /// Footnote reference.
224    FootnoteReference(FootnoteReference),
225    /// Html (phrasing).
226    Html(Html),
227    /// Image.
228    Image(Image),
229    /// Image reference.
230    ImageReference(ImageReference),
231    // MDX: JSX element (text).
232    MdxJsxTextElement(MdxJsxTextElement),
233    /// Link.
234    Link(Link),
235    /// Link reference.
236    LinkReference(LinkReference),
237    /// Strong
238    Strong(Strong),
239    /// Text.
240    Text(Text),
241
242    // Flow:
243    /// Code (flow).
244    Code(Code),
245    /// Math (flow).
246    Math(Math),
247    // MDX: expression (flow).
248    MdxFlowExpression(MdxFlowExpression),
249    /// Heading.
250    Heading(Heading),
251    /// Html (flow).
252    // Html(Html),
253    /// Table.
254    Table(Table),
255    /// Thematic break.
256    ThematicBreak(ThematicBreak),
257
258    // Table content.
259    /// Table row.
260    TableRow(TableRow),
261
262    // Row content.
263    /// Table cell.
264    TableCell(TableCell),
265
266    // List content.
267    /// List item.
268    ListItem(ListItem),
269
270    // Content.
271    /// Definition.
272    Definition(Definition),
273    /// Paragraph.
274    Paragraph(Paragraph),
275}
276
277impl fmt::Debug for Node {
278    // Debug the wrapped struct.
279    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280        match self {
281            Node::Root(x) => x.fmt(f),
282            Node::Fragment(x) => x.fmt(f),
283            Node::Blockquote(x) => x.fmt(f),
284            Node::FootnoteDefinition(x) => x.fmt(f),
285            Node::MdxJsxFlowElement(x) => x.fmt(f),
286            Node::List(x) => x.fmt(f),
287            Node::MdxjsEsm(x) => x.fmt(f),
288            Node::Toml(x) => x.fmt(f),
289            Node::Frontmatter(x) => x.fmt(f),
290            Node::Yaml(x) => x.fmt(f),
291            Node::Break(x) => x.fmt(f),
292            Node::InlineCode(x) => x.fmt(f),
293            Node::InlineMath(x) => x.fmt(f),
294            Node::Delete(x) => x.fmt(f),
295            Node::Emphasis(x) => x.fmt(f),
296            Node::MdxTextExpression(x) => x.fmt(f),
297            Node::FootnoteReference(x) => x.fmt(f),
298            Node::Html(x) => x.fmt(f),
299            Node::Image(x) => x.fmt(f),
300            Node::ImageReference(x) => x.fmt(f),
301            Node::MdxJsxTextElement(x) => x.fmt(f),
302            Node::Link(x) => x.fmt(f),
303            Node::LinkReference(x) => x.fmt(f),
304            Node::Strong(x) => x.fmt(f),
305            Node::Text(x) => x.fmt(f),
306            Node::Code(x) => x.fmt(f),
307            Node::Math(x) => x.fmt(f),
308            Node::MdxFlowExpression(x) => x.fmt(f),
309            Node::Heading(x) => x.fmt(f),
310            Node::Table(x) => x.fmt(f),
311            Node::ThematicBreak(x) => x.fmt(f),
312            Node::TableRow(x) => x.fmt(f),
313            Node::TableCell(x) => x.fmt(f),
314            Node::ListItem(x) => x.fmt(f),
315            Node::Definition(x) => x.fmt(f),
316            Node::Paragraph(x) => x.fmt(f),
317        }
318    }
319}
320
321fn children_to_string(children: &[Node]) -> String {
322    children.iter().map(ToString::to_string).collect()
323}
324
325// To do: clippy may be right but that’s a breaking change.
326#[allow(clippy::to_string_trait_impl)]
327impl ToString for Node {
328    fn to_string(&self) -> String {
329        match self {
330            // Parents.
331            Node::Root(x) => children_to_string(&x.children),
332            Node::Fragment(x) => children_to_string(&x.children),
333            Node::Blockquote(x) => children_to_string(&x.children),
334            Node::FootnoteDefinition(x) => children_to_string(&x.children),
335            Node::MdxJsxFlowElement(x) => children_to_string(&x.children),
336            Node::List(x) => children_to_string(&x.children),
337            Node::Delete(x) => children_to_string(&x.children),
338            Node::Emphasis(x) => children_to_string(&x.children),
339            Node::MdxJsxTextElement(x) => children_to_string(&x.children),
340            Node::Link(x) => children_to_string(&x.children),
341            Node::LinkReference(x) => children_to_string(&x.children),
342            Node::Strong(x) => children_to_string(&x.children),
343            Node::Heading(x) => children_to_string(&x.children),
344            Node::Table(x) => children_to_string(&x.children),
345            Node::TableRow(x) => children_to_string(&x.children),
346            Node::TableCell(x) => children_to_string(&x.children),
347            Node::ListItem(x) => children_to_string(&x.children),
348            Node::Paragraph(x) => children_to_string(&x.children),
349
350            // Literals.
351            Node::MdxjsEsm(x) => x.value.clone(),
352            Node::Toml(x) => x.value.clone(),
353            Node::Frontmatter(x) => x.value.clone(),
354            Node::Yaml(x) => x.value.clone(),
355            Node::InlineCode(x) => x.value.clone(),
356            Node::InlineMath(x) => x.value.clone(),
357            Node::MdxTextExpression(x) => x.value.clone(),
358            Node::Html(x) => x.value.clone(),
359            Node::Text(x) => x.value.clone(),
360            Node::Code(x) => x.value.clone(),
361            Node::Math(x) => x.value.clone(),
362            Node::MdxFlowExpression(x) => x.value.clone(),
363
364            // Voids.
365            Node::Break(_)
366            | Node::FootnoteReference(_)
367            | Node::Image(_)
368            | Node::ImageReference(_)
369            | Node::ThematicBreak(_)
370            | Node::Definition(_) => String::new(),
371        }
372    }
373}
374
375impl Node {
376    #[must_use]
377    pub fn children(&self) -> Option<&Vec<Node>> {
378        match self {
379            // Parent.
380            Node::Root(x) => Some(&x.children),
381            Node::Fragment(x) => Some(&x.children),
382            Node::Paragraph(x) => Some(&x.children),
383            Node::Heading(x) => Some(&x.children),
384            Node::Blockquote(x) => Some(&x.children),
385            Node::List(x) => Some(&x.children),
386            Node::ListItem(x) => Some(&x.children),
387            Node::Emphasis(x) => Some(&x.children),
388            Node::Strong(x) => Some(&x.children),
389            Node::Link(x) => Some(&x.children),
390            Node::LinkReference(x) => Some(&x.children),
391            Node::FootnoteDefinition(x) => Some(&x.children),
392            Node::Table(x) => Some(&x.children),
393            Node::TableRow(x) => Some(&x.children),
394            Node::TableCell(x) => Some(&x.children),
395            Node::Delete(x) => Some(&x.children),
396            Node::MdxJsxFlowElement(x) => Some(&x.children),
397            Node::MdxJsxTextElement(x) => Some(&x.children),
398            // Non-parent.
399            _ => None,
400        }
401    }
402
403    pub fn children_mut(&mut self) -> Option<&mut Vec<Node>> {
404        match self {
405            // Parent.
406            Node::Root(x) => Some(&mut x.children),
407            Node::Fragment(x) => Some(&mut x.children),
408            Node::Paragraph(x) => Some(&mut x.children),
409            Node::Heading(x) => Some(&mut x.children),
410            Node::Blockquote(x) => Some(&mut x.children),
411            Node::List(x) => Some(&mut x.children),
412            Node::ListItem(x) => Some(&mut x.children),
413            Node::Emphasis(x) => Some(&mut x.children),
414            Node::Strong(x) => Some(&mut x.children),
415            Node::Link(x) => Some(&mut x.children),
416            Node::LinkReference(x) => Some(&mut x.children),
417            Node::FootnoteDefinition(x) => Some(&mut x.children),
418            Node::Table(x) => Some(&mut x.children),
419            Node::TableRow(x) => Some(&mut x.children),
420            Node::TableCell(x) => Some(&mut x.children),
421            Node::Delete(x) => Some(&mut x.children),
422            Node::MdxJsxFlowElement(x) => Some(&mut x.children),
423            Node::MdxJsxTextElement(x) => Some(&mut x.children),
424            // Non-parent.
425            _ => None,
426        }
427    }
428
429    #[must_use]
430    pub fn position(&self) -> Option<&Position> {
431        match self {
432            Node::Root(x) => x.position.as_ref(),
433            Node::Fragment(_) => None,
434            Node::Blockquote(x) => x.position.as_ref(),
435            Node::FootnoteDefinition(x) => x.position.as_ref(),
436            Node::MdxJsxFlowElement(x) => x.position.as_ref(),
437            Node::List(x) => x.position.as_ref(),
438            Node::MdxjsEsm(x) => x.position.as_ref(),
439            Node::Toml(x) => x.position.as_ref(),
440            Node::Frontmatter(x) => x.position.as_ref(),
441            Node::Yaml(x) => x.position.as_ref(),
442            Node::Break(x) => x.position.as_ref(),
443            Node::InlineCode(x) => x.position.as_ref(),
444            Node::InlineMath(x) => x.position.as_ref(),
445            Node::Delete(x) => x.position.as_ref(),
446            Node::Emphasis(x) => x.position.as_ref(),
447            Node::MdxTextExpression(x) => x.position.as_ref(),
448            Node::FootnoteReference(x) => x.position.as_ref(),
449            Node::Html(x) => x.position.as_ref(),
450            Node::Image(x) => x.position.as_ref(),
451            Node::ImageReference(x) => x.position.as_ref(),
452            Node::MdxJsxTextElement(x) => x.position.as_ref(),
453            Node::Link(x) => x.position.as_ref(),
454            Node::LinkReference(x) => x.position.as_ref(),
455            Node::Strong(x) => x.position.as_ref(),
456            Node::Text(x) => x.position.as_ref(),
457            Node::Code(x) => x.position.as_ref(),
458            Node::Math(x) => x.position.as_ref(),
459            Node::MdxFlowExpression(x) => x.position.as_ref(),
460            Node::Heading(x) => x.position.as_ref(),
461            Node::Table(x) => x.position.as_ref(),
462            Node::ThematicBreak(x) => x.position.as_ref(),
463            Node::TableRow(x) => x.position.as_ref(),
464            Node::TableCell(x) => x.position.as_ref(),
465            Node::ListItem(x) => x.position.as_ref(),
466            Node::Definition(x) => x.position.as_ref(),
467            Node::Paragraph(x) => x.position.as_ref(),
468        }
469    }
470
471    pub fn position_mut(&mut self) -> Option<&mut Position> {
472        match self {
473            Node::Root(x) => x.position.as_mut(),
474            Node::Fragment(_) => None,
475            Node::Blockquote(x) => x.position.as_mut(),
476            Node::FootnoteDefinition(x) => x.position.as_mut(),
477            Node::MdxJsxFlowElement(x) => x.position.as_mut(),
478            Node::List(x) => x.position.as_mut(),
479            Node::MdxjsEsm(x) => x.position.as_mut(),
480            Node::Toml(x) => x.position.as_mut(),
481            Node::Frontmatter(x) => x.position.as_mut(),
482            Node::Yaml(x) => x.position.as_mut(),
483            Node::Break(x) => x.position.as_mut(),
484            Node::InlineCode(x) => x.position.as_mut(),
485            Node::InlineMath(x) => x.position.as_mut(),
486            Node::Delete(x) => x.position.as_mut(),
487            Node::Emphasis(x) => x.position.as_mut(),
488            Node::MdxTextExpression(x) => x.position.as_mut(),
489            Node::FootnoteReference(x) => x.position.as_mut(),
490            Node::Html(x) => x.position.as_mut(),
491            Node::Image(x) => x.position.as_mut(),
492            Node::ImageReference(x) => x.position.as_mut(),
493            Node::MdxJsxTextElement(x) => x.position.as_mut(),
494            Node::Link(x) => x.position.as_mut(),
495            Node::LinkReference(x) => x.position.as_mut(),
496            Node::Strong(x) => x.position.as_mut(),
497            Node::Text(x) => x.position.as_mut(),
498            Node::Code(x) => x.position.as_mut(),
499            Node::Math(x) => x.position.as_mut(),
500            Node::MdxFlowExpression(x) => x.position.as_mut(),
501            Node::Heading(x) => x.position.as_mut(),
502            Node::Table(x) => x.position.as_mut(),
503            Node::ThematicBreak(x) => x.position.as_mut(),
504            Node::TableRow(x) => x.position.as_mut(),
505            Node::TableCell(x) => x.position.as_mut(),
506            Node::ListItem(x) => x.position.as_mut(),
507            Node::Definition(x) => x.position.as_mut(),
508            Node::Paragraph(x) => x.position.as_mut(),
509        }
510    }
511
512    pub fn position_set(&mut self, position: Option<Position>) {
513        match self {
514            Node::Root(x) => x.position = position,
515            Node::Fragment(_) => (),
516            Node::Blockquote(x) => x.position = position,
517            Node::FootnoteDefinition(x) => x.position = position,
518            Node::MdxJsxFlowElement(x) => x.position = position,
519            Node::List(x) => x.position = position,
520            Node::MdxjsEsm(x) => x.position = position,
521            Node::Toml(x) => x.position = position,
522            Node::Frontmatter(x) => x.position = position,
523            Node::Yaml(x) => x.position = position,
524            Node::Break(x) => x.position = position,
525            Node::InlineCode(x) => x.position = position,
526            Node::InlineMath(x) => x.position = position,
527            Node::Delete(x) => x.position = position,
528            Node::Emphasis(x) => x.position = position,
529            Node::MdxTextExpression(x) => x.position = position,
530            Node::FootnoteReference(x) => x.position = position,
531            Node::Html(x) => x.position = position,
532            Node::Image(x) => x.position = position,
533            Node::ImageReference(x) => x.position = position,
534            Node::MdxJsxTextElement(x) => x.position = position,
535            Node::Link(x) => x.position = position,
536            Node::LinkReference(x) => x.position = position,
537            Node::Strong(x) => x.position = position,
538            Node::Text(x) => x.position = position,
539            Node::Code(x) => x.position = position,
540            Node::Math(x) => x.position = position,
541            Node::MdxFlowExpression(x) => x.position = position,
542            Node::Heading(x) => x.position = position,
543            Node::Table(x) => x.position = position,
544            Node::ThematicBreak(x) => x.position = position,
545            Node::TableRow(x) => x.position = position,
546            Node::TableCell(x) => x.position = position,
547            Node::ListItem(x) => x.position = position,
548            Node::Definition(x) => x.position = position,
549            Node::Paragraph(x) => x.position = position,
550        }
551    }
552
553    /// Visit this node and its descendants in depth-first, pre-order.
554    pub fn walk<'a>(&'a self, mut visitor: impl FnMut(&'a Node)) {
555        fn visit<'a>(node: &'a Node, visitor: &mut impl FnMut(&'a Node)) {
556            visitor(node);
557            if let Some(children) = node.children() {
558                for child in children {
559                    visit(child, visitor);
560                }
561            }
562        }
563
564        visit(self, &mut visitor);
565    }
566
567    /// Mutably visit this node and its descendants in depth-first, pre-order.
568    pub fn walk_mut(&mut self, mut visitor: impl FnMut(&mut Node)) {
569        fn visit(node: &mut Node, visitor: &mut impl FnMut(&mut Node)) {
570            visitor(node);
571            if let Some(children) = node.children_mut() {
572                for child in children {
573                    visit(child, visitor);
574                }
575            }
576        }
577
578        visit(self, &mut visitor);
579    }
580
581    /// Return the concatenated text represented by this node.
582    ///
583    /// Image nodes contribute their alt text and hard breaks contribute a
584    /// newline. Structural nodes concatenate the text of their children.
585    #[must_use]
586    pub fn text_content(&self) -> String {
587        match self {
588            Node::Image(node) => node.alt.clone(),
589            Node::ImageReference(node) => node.alt.clone(),
590            Node::Break(_) => "\n".into(),
591            Node::Definition(_) | Node::FootnoteReference(_) | Node::ThematicBreak(_) => {
592                String::new()
593            }
594            _ => {
595                if let Some(children) = self.children() {
596                    children.iter().map(Node::text_content).collect()
597                } else {
598                    self.to_string()
599                }
600            }
601        }
602    }
603
604    /// Find a direct child heading with the requested text.
605    #[must_use]
606    pub fn find_heading(&self, title: &str) -> Option<&Node> {
607        self.children()?
608            .iter()
609            .find(|child| matches!(child, Node::Heading(_)) && child.text_content() == title)
610    }
611
612    /// Return the language identifier of inline code, fenced code, or tagged frontmatter.
613    #[must_use]
614    pub fn code_language(&self) -> Option<&str> {
615        match self {
616            Node::InlineCode(node) => node.lang.as_deref(),
617            Node::Code(node) => node.lang.as_deref(),
618            Node::Frontmatter(node) => node.language(),
619            _ => None,
620        }
621    }
622
623    /// Return the info string for inline code, fenced code, or tagged frontmatter.
624    ///
625    /// Inline code metadata is its language token. Fenced code metadata is
626    /// reconstructed from `lang` and `meta` when either is present. Tagged
627    /// frontmatter returns its opaque info string.
628    #[must_use]
629    pub fn code_info(&self) -> Option<String> {
630        match self {
631            Node::InlineCode(node) => node.lang.clone(),
632            Node::Frontmatter(node) => Some(node.info.clone()),
633            Node::Code(node) => match (&node.lang, &node.meta) {
634                (Some(lang), Some(meta)) => Some(alloc::format!("{lang} {meta}")),
635                (Some(lang), None) => Some(lang.clone()),
636                (None, Some(meta)) => Some(meta.clone()),
637                (None, None) => None,
638            },
639            _ => None,
640        }
641    }
642
643    /// Take all direct children from this node and return them in a fragment.
644    ///
645    /// Returns `None` for nodes that cannot contain children. The extracted
646    /// child nodes retain their own positions; the new fragment has no source
647    /// position of its own.
648    pub fn extract_children(&mut self) -> Option<Node> {
649        self.children_mut().map(|children| {
650            Node::Fragment(Fragment {
651                children: core::mem::take(children),
652            })
653        })
654    }
655
656    /// Render this node with a user-provided renderer.
657    pub fn render_with<R: crate::renderer::Renderer>(&self, renderer: &mut R) -> String {
658        renderer.render(self)
659    }
660
661    /// Serialize this AST node or fragment as Markdown.
662    ///
663    /// Markdown output ends with a newline. For custom formatting, use
664    /// [`to_markdown_with_options`][crate::markdown::to_markdown_with_options].
665    ///
666    /// # Example
667    ///
668    /// ```ignore
669    /// let markdown = node.to_markdown();
670    /// ```
671    #[must_use]
672    pub fn to_markdown(&self) -> String {
673        crate::markdown::to_markdown(self).expect("Markdown serialization failed for this AST node")
674    }
675
676    /// Serialize this AST node or fragment as Markdown with custom options.
677    ///
678    /// # Errors
679    ///
680    /// Returns an error if an option contains an invalid marker or the AST
681    /// contains a node that cannot be serialized.
682    pub fn to_markdown_with_options(
683        &self,
684        options: &crate::markdown::Options,
685    ) -> Result<String, crate::message::Message> {
686        crate::markdown::to_markdown_with_options(self, options)
687    }
688
689    /// Replace a heading and the section below it in this node's children.
690    ///
691    /// `heading_index` is the direct-child index of the heading. When
692    /// `replace_heading` is false, the heading stays and only its section
693    /// content is replaced. When `remove_subsections` is true, nested heading
694    /// sections are included through the next heading of the same or higher
695    /// level. Returns `false` if the index is not a heading.
696    pub fn replace_heading_section(
697        &mut self,
698        heading_index: usize,
699        replacement: Vec<Node>,
700        replace_heading: bool,
701        remove_subsections: bool,
702    ) -> bool {
703        let Some(children) = self.children_mut() else {
704            return false;
705        };
706        let depth = match children.get(heading_index) {
707            Some(Node::Heading(heading)) => heading.depth,
708            _ => return false,
709        };
710        let end = children
711            .iter()
712            .enumerate()
713            .skip(heading_index + 1)
714            .find_map(|(index, node)| {
715                let Node::Heading(next) = node else {
716                    return None;
717                };
718                if !remove_subsections || next.depth <= depth {
719                    Some(index)
720                } else {
721                    None
722                }
723            })
724            .unwrap_or(children.len());
725        let start = if replace_heading {
726            heading_index
727        } else {
728            heading_index + 1
729        };
730
731        children.splice(start..end, replacement);
732        true
733    }
734}
735
736/// MDX: attribute content.
737#[derive(Clone, Debug, Eq, PartialEq)]
738#[cfg_attr(
739    feature = "serde",
740    derive(serde::Serialize, serde::Deserialize),
741    serde(untagged)
742)]
743pub enum AttributeContent {
744    /// JSX expression.
745    ///
746    /// ```markdown
747    /// > | <a {...b} />
748    ///        ^^^^^^
749    /// ```
750    Expression(MdxJsxExpressionAttribute),
751    /// JSX property.
752    ///
753    /// ```markdown
754    /// > | <a b />
755    ///        ^
756    /// ```
757    Property(MdxJsxAttribute),
758}
759//
760#[derive(Clone, Debug, Eq, PartialEq)]
761#[cfg_attr(
762    feature = "serde",
763    derive(serde::Serialize, serde::Deserialize),
764    serde(tag = "type", rename = "mdxJsxAttributeValueExpression")
765)]
766pub struct AttributeValueExpression {
767    pub value: String,
768    #[cfg_attr(feature = "serde", serde(rename = "_markdownRsStops"))]
769    pub stops: Vec<Stop>,
770}
771
772/// MDX: attribute value.
773#[derive(Clone, Debug, Eq, PartialEq)]
774#[cfg_attr(
775    feature = "serde",
776    derive(serde::Serialize, serde::Deserialize),
777    serde(untagged)
778)]
779pub enum AttributeValue {
780    /// Expression value.
781    ///
782    /// ```markdown
783    /// > | <a b={c} />
784    ///          ^^^
785    /// ```
786    Expression(AttributeValueExpression),
787    /// Static value.
788    ///
789    /// ```markdown
790    /// > | <a b="c" />
791    ///          ^^^
792    /// ```
793    Literal(String),
794}
795
796/// A transparent container for a sequence of AST nodes.
797///
798/// Fragments have no source position of their own. They are useful for holding
799/// the children extracted from another node.
800#[derive(Clone, Debug, Eq, PartialEq)]
801#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
802pub struct Fragment {
803    /// Content model.
804    pub children: Vec<Node>,
805}
806
807/// Document.
808///
809/// ```markdown
810/// > | a
811///     ^
812/// ```
813#[derive(Clone, Debug, Eq, PartialEq)]
814#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
815pub struct Root {
816    // Parent.
817    /// Content model.
818    pub children: Vec<Node>,
819    /// Positional info.
820    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
821    pub position: Option<Position>,
822}
823
824/// Paragraph.
825///
826/// ```markdown
827/// > | a
828///     ^
829/// ```
830#[derive(Clone, Debug, Eq, PartialEq)]
831#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
832pub struct Paragraph {
833    // Parent.
834    /// Content model.
835    pub children: Vec<Node>,
836    /// Positional info.
837    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
838    pub position: Option<Position>,
839}
840
841/// Heading.
842///
843/// ```markdown
844/// > | # a
845///     ^^^
846/// ```
847#[derive(Clone, Debug, Eq, PartialEq)]
848#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
849pub struct Heading {
850    // Parent.
851    /// Content model.
852    pub children: Vec<Node>,
853    /// Positional info.
854    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
855    pub position: Option<Position>,
856    // Extra.
857    /// Rank (between `1` and `6`, both including).
858    pub depth: u8,
859}
860
861/// Thematic break.
862///
863/// ```markdown
864/// > | ***
865///     ^^^
866/// ```
867#[derive(Clone, Debug, Eq, PartialEq)]
868#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
869pub struct ThematicBreak {
870    // Void.
871    /// Positional info.
872    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
873    pub position: Option<Position>,
874}
875
876/// Block quote.
877///
878/// ```markdown
879/// > | > a
880///     ^^^
881/// ```
882#[derive(Clone, Debug, Eq, PartialEq)]
883#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
884pub struct Blockquote {
885    // Parent.
886    /// Content model.
887    pub children: Vec<Node>,
888    /// Positional info.
889    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
890    pub position: Option<Position>,
891}
892
893/// List.
894///
895/// ```markdown
896/// > | * a
897///     ^^^
898/// ```
899#[derive(Clone, Debug, Eq, PartialEq)]
900#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
901pub struct List {
902    // Parent.
903    /// Content model.
904    pub children: Vec<Node>,
905    /// Positional info.
906    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
907    pub position: Option<Position>,
908    // Extra.
909    /// Ordered (`true`) or unordered (`false`).
910    pub ordered: bool,
911    /// Starting number of the list.
912    /// `None` when unordered.
913    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
914    pub start: Option<u32>,
915    /// One or more of its children are separated with a blank line from its
916    /// siblings (when `true`), or not (when `false`).
917    pub spread: bool,
918}
919
920/// List item.
921///
922/// ```markdown
923/// > | * a
924///     ^^^
925/// ```
926#[derive(Clone, Debug, Eq, PartialEq)]
927#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
928pub struct ListItem {
929    // Parent.
930    /// Content model.
931    pub children: Vec<Node>,
932    /// Positional info.
933    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
934    pub position: Option<Position>,
935    // Extra.
936    /// The item contains two or more children separated by a blank line
937    /// (when `true`), or not (when `false`).
938    pub spread: bool,
939    /// GFM: whether the item is done (when `true`), not done (when `false`),
940    /// or indeterminate or not applicable (`None`).
941    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
942    pub checked: Option<bool>,
943}
944
945/// Html (flow or phrasing).
946///
947/// ```markdown
948/// > | <a>
949///     ^^^
950/// ```
951#[derive(Clone, Debug, Eq, PartialEq)]
952#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
953pub struct Html {
954    // Text.
955    /// Content model.
956    pub value: String,
957    /// Positional info.
958    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
959    pub position: Option<Position>,
960}
961
962/// Code (flow).
963///
964/// ```markdown
965/// > | ~~~
966///     ^^^
967/// > | a
968///     ^
969/// > | ~~~
970///     ^^^
971/// ```
972#[derive(Clone, Debug, Eq, PartialEq)]
973#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
974pub struct Code {
975    // Text.
976    /// Content model.
977    pub value: String,
978    /// Positional info.
979    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
980    pub position: Option<Position>,
981    // Extra.
982    /// The language of computer code being marked up.
983    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
984    pub lang: Option<String>,
985    /// Custom info relating to the node.
986    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
987    pub meta: Option<String>,
988}
989
990/// Math (flow).
991///
992/// ```markdown
993/// > | $$
994///     ^^
995/// > | a
996///     ^
997/// > | $$
998///     ^^
999/// ```
1000#[derive(Clone, Debug, Eq, PartialEq)]
1001#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1002pub struct Math {
1003    // Text.
1004    /// Content model.
1005    pub value: String,
1006    /// Positional info.
1007    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1008    pub position: Option<Position>,
1009    // Extra.
1010    /// Custom info relating to the node.
1011    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1012    pub meta: Option<String>,
1013}
1014
1015/// Definition.
1016///
1017/// ```markdown
1018/// > | [a]: b
1019///     ^^^^^^
1020/// ```
1021#[derive(Clone, Debug, Eq, PartialEq)]
1022#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1023pub struct Definition {
1024    // Void.
1025    /// Positional info.
1026    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1027    pub position: Option<Position>,
1028    // Resource.
1029    /// URL to the referenced resource.
1030    pub url: String,
1031    /// Advisory info for the resource, such as something that would be
1032    /// appropriate for a tooltip.
1033    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1034    pub title: Option<String>,
1035    // Association.
1036    /// Value that can match another node.
1037    /// `identifier` is a source value: character escapes and character references
1038    /// are *not* parsed.
1039    /// Its value must be normalized.
1040    pub identifier: String,
1041    /// `label` is a string value: it works just like `title` on a link or a
1042    /// `lang` on code: character escapes and character references are parsed.
1043    ///
1044    /// To normalize a value, collapse markdown whitespace (`[\t\n\r ]+`) to a
1045    /// space, trim the optional initial and/or final space, and perform
1046    /// case-folding.
1047    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1048    pub label: Option<String>,
1049}
1050
1051/// Text.
1052///
1053/// ```markdown
1054/// > | a
1055///     ^
1056/// ```
1057#[derive(Clone, Debug, Eq, PartialEq)]
1058#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1059pub struct Text {
1060    // Text.
1061    /// Content model.
1062    pub value: String,
1063    /// Positional info.
1064    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1065    pub position: Option<Position>,
1066}
1067
1068/// Emphasis.
1069///
1070/// ```markdown
1071/// > | *a*
1072///     ^^^
1073/// ```
1074#[derive(Clone, Debug, Eq, PartialEq)]
1075#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1076pub struct Emphasis {
1077    // Parent.
1078    /// Content model.
1079    pub children: Vec<Node>,
1080    /// Positional info.
1081    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1082    pub position: Option<Position>,
1083}
1084
1085/// Strong.
1086///
1087/// ```markdown
1088/// > | **a**
1089///     ^^^^^
1090/// ```
1091#[derive(Clone, Debug, Eq, PartialEq)]
1092#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1093pub struct Strong {
1094    // Parent.
1095    /// Content model.
1096    pub children: Vec<Node>,
1097    /// Positional info.
1098    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1099    pub position: Option<Position>,
1100}
1101
1102/// Code (phrasing).
1103///
1104/// ```markdown
1105/// > | `a`
1106///     ^^^
1107/// ```
1108#[derive(Clone, Debug, Eq, PartialEq)]
1109#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1110pub struct InlineCode {
1111    // Text.
1112    /// Content model.
1113    pub value: String,
1114    /// Positional info.
1115    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1116    pub position: Option<Position>,
1117    /// Optional language identifier parsed from an inline code prefix.
1118    ///
1119    /// This is a Socketry Markdown extension; standard mdast inline code
1120    /// nodes do not have a language field.
1121    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1122    pub lang: Option<String>,
1123}
1124
1125/// Math (phrasing).
1126///
1127/// ```markdown
1128/// > | $a$
1129///     ^^^
1130/// ```
1131#[derive(Clone, Debug, Eq, PartialEq)]
1132#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1133pub struct InlineMath {
1134    // Text.
1135    /// Content model.
1136    pub value: String,
1137    /// Positional info.
1138    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1139    pub position: Option<Position>,
1140}
1141
1142/// Break.
1143///
1144/// ```markdown
1145/// > | a\
1146///      ^
1147///   | b
1148/// ```
1149#[derive(Clone, Debug, Eq, PartialEq)]
1150#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1151pub struct Break {
1152    // Void.
1153    /// Positional info.
1154    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1155    pub position: Option<Position>,
1156}
1157
1158/// Link.
1159///
1160/// ```markdown
1161/// > | [a](b)
1162///     ^^^^^^
1163/// ```
1164#[derive(Clone, Debug, Eq, PartialEq)]
1165#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1166pub struct Link {
1167    // Parent.
1168    /// Content model.
1169    pub children: Vec<Node>,
1170    /// Positional info.
1171    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1172    pub position: Option<Position>,
1173    // Resource.
1174    /// URL to the referenced resource.
1175    pub url: String,
1176    /// Advisory info for the resource, such as something that would be
1177    /// appropriate for a tooltip.
1178    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1179    pub title: Option<String>,
1180}
1181
1182/// Image.
1183///
1184/// ```markdown
1185/// > | ![a](b)
1186///     ^^^^^^^
1187/// ```
1188#[derive(Clone, Debug, Eq, PartialEq)]
1189#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1190pub struct Image {
1191    // Void.
1192    /// Positional info.
1193    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1194    pub position: Option<Position>,
1195    // Alternative.
1196    /// Equivalent content for environments that cannot represent the node as
1197    /// intended.
1198    pub alt: String,
1199    // Resource.
1200    /// URL to the referenced resource.
1201    pub url: String,
1202    /// Advisory info for the resource, such as something that would be
1203    /// appropriate for a tooltip.
1204    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1205    pub title: Option<String>,
1206}
1207
1208/// Link reference.
1209///
1210/// ```markdown
1211/// > | [a]
1212///     ^^^
1213/// ```
1214#[derive(Clone, Debug, Eq, PartialEq)]
1215#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1216pub struct LinkReference {
1217    // Parent.
1218    /// Content model.
1219    pub children: Vec<Node>,
1220    /// Positional info.
1221    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1222    pub position: Option<Position>,
1223    // Reference.
1224    /// Explicitness of a reference.
1225    #[cfg_attr(feature = "serde", serde(rename = "referenceType"))]
1226    pub reference_kind: ReferenceKind,
1227    // Association.
1228    /// Value that can match another node.
1229    /// `identifier` is a source value: character escapes and character references
1230    /// are *not* parsed.
1231    /// Its value must be normalized.
1232    pub identifier: String,
1233    /// `label` is a string value: it works just like `title` on a link or a
1234    /// `lang` on code: character escapes and character references are parsed.
1235    ///
1236    /// To normalize a value, collapse markdown whitespace (`[\t\n\r ]+`) to a
1237    /// space, trim the optional initial and/or final space, and perform
1238    /// case-folding.
1239    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1240    pub label: Option<String>,
1241}
1242
1243/// Image reference.
1244///
1245/// ```markdown
1246/// > | ![a]
1247///     ^^^^
1248/// ```
1249#[derive(Clone, Debug, Eq, PartialEq)]
1250#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1251pub struct ImageReference {
1252    // Void.
1253    /// Positional info.
1254    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1255    pub position: Option<Position>,
1256    // Alternative.
1257    /// Equivalent content for environments that cannot represent the node as
1258    /// intended.
1259    pub alt: String,
1260    // Reference.
1261    /// Explicitness of a reference.
1262    #[cfg_attr(feature = "serde", serde(rename = "referenceType"))]
1263    pub reference_kind: ReferenceKind,
1264    // Association.
1265    /// Value that can match another node.
1266    /// `identifier` is a source value: character escapes and character references
1267    /// are *not* parsed.
1268    /// Its value must be normalized.
1269    pub identifier: String,
1270    /// `label` is a string value: it works just like `title` on a link or a
1271    /// `lang` on code: character escapes and character references are parsed.
1272    ///
1273    /// To normalize a value, collapse markdown whitespace (`[\t\n\r ]+`) to a
1274    /// space, trim the optional initial and/or final space, and perform
1275    /// case-folding.
1276    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1277    pub label: Option<String>,
1278}
1279
1280/// GFM: footnote definition.
1281///
1282/// ```markdown
1283/// > | [^a]: b
1284///     ^^^^^^^
1285/// ```
1286#[derive(Clone, Debug, Eq, PartialEq)]
1287#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1288pub struct FootnoteDefinition {
1289    // Parent.
1290    /// Content model.
1291    pub children: Vec<Node>,
1292    /// Positional info.
1293    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1294    pub position: Option<Position>,
1295    // Association.
1296    /// Value that can match another node.
1297    /// `identifier` is a source value: character escapes and character references
1298    /// are *not* parsed.
1299    /// Its value must be normalized.
1300    pub identifier: String,
1301    /// `label` is a string value: it works just like `title` on a link or a
1302    /// `lang` on code: character escapes and character references are parsed.
1303    ///
1304    /// To normalize a value, collapse markdown whitespace (`[\t\n\r ]+`) to a
1305    /// space, trim the optional initial and/or final space, and perform
1306    /// case-folding.
1307    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1308    pub label: Option<String>,
1309}
1310
1311/// GFM: footnote reference.
1312///
1313/// ```markdown
1314/// > | [^a]
1315///     ^^^^
1316/// ```
1317#[derive(Clone, Debug, Eq, PartialEq)]
1318#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1319pub struct FootnoteReference {
1320    // Void.
1321    /// Positional info.
1322    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1323    pub position: Option<Position>,
1324    // Association.
1325    /// Value that can match another node.
1326    /// `identifier` is a source value: character escapes and character references
1327    /// are *not* parsed.
1328    /// Its value must be normalized.
1329    pub identifier: String,
1330    /// `label` is a string value: it works just like `title` on a link or a
1331    /// `lang` on code: character escapes and character references are parsed.
1332    ///
1333    /// To normalize a value, collapse markdown whitespace (`[\t\n\r ]+`) to a
1334    /// space, trim the optional initial and/or final space, and perform
1335    /// case-folding.
1336    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1337    pub label: Option<String>,
1338}
1339
1340/// GFM: table.
1341///
1342/// ```markdown
1343/// > | | a |
1344///     ^^^^^
1345/// > | | - |
1346///     ^^^^^
1347/// ```
1348#[derive(Clone, Debug, Eq, PartialEq)]
1349#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1350pub struct Table {
1351    // Parent.
1352    /// Content model.
1353    pub children: Vec<Node>,
1354    /// Positional info.
1355    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1356    pub position: Option<Position>,
1357    // Extra.
1358    /// Represents how cells in columns are aligned.
1359    pub align: Vec<AlignKind>,
1360}
1361
1362/// GFM: table row.
1363///
1364/// ```markdown
1365/// > | | a |
1366///     ^^^^^
1367/// ```
1368#[derive(Clone, Debug, Eq, PartialEq)]
1369#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1370pub struct TableRow {
1371    // Parent.
1372    /// Content model.
1373    pub children: Vec<Node>,
1374    /// Positional info.
1375    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1376    pub position: Option<Position>,
1377}
1378
1379/// GFM: table cell.
1380///
1381/// ```markdown
1382/// > | | a |
1383///     ^^^^^
1384/// ```
1385#[derive(Clone, Debug, Eq, PartialEq)]
1386#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1387pub struct TableCell {
1388    // Parent.
1389    /// Content model.
1390    pub children: Vec<Node>,
1391    /// Positional info.
1392    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1393    pub position: Option<Position>,
1394}
1395
1396/// GFM: delete.
1397///
1398/// ```markdown
1399/// > | ~~a~~
1400///     ^^^^^
1401/// ```
1402#[derive(Clone, Debug, Eq, PartialEq)]
1403#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1404pub struct Delete {
1405    // Parent.
1406    /// Content model.
1407    pub children: Vec<Node>,
1408    /// Positional info.
1409    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1410    pub position: Option<Position>,
1411}
1412
1413/// Frontmatter: yaml.
1414///
1415/// ```markdown
1416/// > | ---
1417///     ^^^
1418/// > | a: b
1419///     ^^^^
1420/// > | ---
1421///     ^^^
1422/// ```
1423#[derive(Clone, Debug, Eq, PartialEq)]
1424#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1425pub struct Yaml {
1426    // Void.
1427    /// Content model.
1428    pub value: String,
1429    /// Positional info.
1430    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1431    pub position: Option<Position>,
1432}
1433
1434/// Frontmatter: toml.
1435///
1436/// ```markdown
1437/// > | +++
1438///     ^^^
1439/// > | a: b
1440///     ^^^^
1441/// > | +++
1442///     ^^^
1443/// ```
1444#[derive(Clone, Debug, Eq, PartialEq)]
1445#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1446pub struct Toml {
1447    // Void.
1448    /// Content model.
1449    pub value: String,
1450    /// Positional info.
1451    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1452    pub position: Option<Position>,
1453}
1454
1455/// MDX: ESM.
1456///
1457/// ```markdown
1458/// > | import a from 'b'
1459///     ^^^^^^^^^^^^^^^^^
1460/// ```
1461#[derive(Clone, Debug, Eq, PartialEq)]
1462#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1463pub struct MdxjsEsm {
1464    // Literal.
1465    /// Content model.
1466    pub value: String,
1467    /// Positional info.
1468    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1469    pub position: Option<Position>,
1470
1471    // Custom data on where each slice of `value` came from.
1472    #[cfg_attr(feature = "serde", serde(rename = "_markdownRsStops"))]
1473    pub stops: Vec<Stop>,
1474}
1475
1476/// MDX: expression (flow).
1477///
1478/// ```markdown
1479/// > | {a}
1480///     ^^^
1481/// ```
1482#[derive(Clone, Debug, Eq, PartialEq)]
1483#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1484pub struct MdxFlowExpression {
1485    // Literal.
1486    /// Content model.
1487    pub value: String,
1488    /// Positional info.
1489    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1490    pub position: Option<Position>,
1491
1492    // Custom data on where each slice of `value` came from.
1493    #[cfg_attr(feature = "serde", serde(rename = "_markdownRsStops"))]
1494    pub stops: Vec<Stop>,
1495}
1496
1497/// MDX: expression (text).
1498///
1499/// ```markdown
1500/// > | a {b}
1501///       ^^^
1502/// ```
1503#[derive(Clone, Debug, Eq, PartialEq)]
1504#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1505pub struct MdxTextExpression {
1506    // Literal.
1507    /// Content model.
1508    pub value: String,
1509    /// Positional info.
1510    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1511    pub position: Option<Position>,
1512
1513    // Custom data on where each slice of `value` came from.
1514    #[cfg_attr(feature = "serde", serde(rename = "_markdownRsStops"))]
1515    pub stops: Vec<Stop>,
1516}
1517
1518/// MDX: JSX element (container).
1519///
1520/// ```markdown
1521/// > | <a />
1522///     ^^^^^
1523/// ```
1524#[derive(Clone, Debug, Eq, PartialEq)]
1525#[cfg_attr(
1526    feature = "serde",
1527    derive(serde::Serialize, serde::Deserialize),
1528    serde(rename_all = "camelCase")
1529)]
1530pub struct MdxJsxFlowElement {
1531    // Parent.
1532    /// Content model.
1533    pub children: Vec<Node>,
1534    /// Positional info.
1535    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1536    pub position: Option<Position>,
1537    // JSX element.
1538    /// Name.
1539    ///
1540    /// Fragments have no name.
1541    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1542    pub name: Option<String>,
1543    /// Attributes.
1544    pub attributes: Vec<AttributeContent>,
1545}
1546
1547/// MDX: JSX element (text).
1548///
1549/// ```markdown
1550/// > | <a />.
1551///     ^^^^^
1552/// ```
1553#[derive(Clone, Debug, Eq, PartialEq)]
1554#[cfg_attr(
1555    feature = "serde",
1556    derive(serde::Serialize, serde::Deserialize),
1557    serde(rename_all = "camelCase")
1558)]
1559pub struct MdxJsxTextElement {
1560    // Parent.
1561    /// Content model.
1562    pub children: Vec<Node>,
1563    /// Positional info.
1564    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1565    pub position: Option<Position>,
1566    // JSX element.
1567    /// Name.
1568    ///
1569    /// Fragments have no name.
1570    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1571    pub name: Option<String>,
1572    /// Attributes.
1573    pub attributes: Vec<AttributeContent>,
1574}
1575
1576/// MDX: JSX attribute.
1577///
1578/// ```markdown
1579/// > | <a b />
1580///        ^
1581/// ```
1582#[derive(Clone, Debug, Eq, PartialEq)]
1583#[cfg_attr(
1584    feature = "serde",
1585    derive(serde::Serialize, serde::Deserialize),
1586    serde(tag = "type", rename = "mdxJsxAttribute")
1587)]
1588pub struct MdxJsxAttribute {
1589    // Void.
1590    /// Positional info.
1591    // pub position: Option<Position>,
1592    /// Key.
1593    pub name: String,
1594    /// Value.
1595    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
1596    pub value: Option<AttributeValue>,
1597}
1598
1599/// MDX: JSX expression attribute.
1600///
1601/// ```markdown
1602/// > | <a {...b} />
1603///        ^
1604/// ```
1605#[derive(Clone, Debug, Eq, PartialEq)]
1606#[cfg_attr(
1607    feature = "serde",
1608    derive(serde::Serialize, serde::Deserialize),
1609    serde(tag = "type", rename = "mdxJsxExpressionAttribute")
1610)]
1611pub struct MdxJsxExpressionAttribute {
1612    /// Value.
1613    pub value: String,
1614    /// Stops
1615    #[cfg_attr(feature = "serde", serde(rename = "_markdownRsStops"))]
1616    pub stops: Vec<Stop>,
1617}
1618
1619#[cfg(test)]
1620mod tests {
1621    use super::*;
1622    use crate::unist::Position;
1623    use alloc::{format, string::ToString, vec};
1624
1625    // Literals.
1626
1627    #[test]
1628    fn text() {
1629        let mut node = Node::Text(Text {
1630            value: "a".into(),
1631            position: None,
1632        });
1633
1634        assert_eq!(
1635            format!("{node:?}"),
1636            "Text { value: \"a\", position: None }",
1637            "should support `Debug`"
1638        );
1639        assert_eq!(node.to_string(), "a", "should support `ToString`");
1640        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1641        assert_eq!(node.children(), None, "should support `children`");
1642        assert_eq!(node.position(), None, "should support `position`");
1643        assert_eq!(node.position_mut(), None, "should support `position`");
1644        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1645        assert_eq!(
1646            format!("{node:?}"),
1647            "Text { value: \"a\", position: Some(1:1-1:2 (0-1)) }",
1648            "should support `position_set`"
1649        );
1650    }
1651
1652    #[test]
1653    fn inline_code() {
1654        let mut node = Node::InlineCode(InlineCode {
1655            value: "a".into(),
1656            position: None,
1657            lang: None,
1658        });
1659
1660        assert_eq!(
1661            format!("{node:?}"),
1662            "InlineCode { value: \"a\", position: None, lang: None }",
1663            "should support `Debug`"
1664        );
1665        assert_eq!(node.to_string(), "a", "should support `ToString`");
1666        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1667        assert_eq!(node.children(), None, "should support `children`");
1668        assert_eq!(node.position(), None, "should support `position`");
1669        assert_eq!(node.position_mut(), None, "should support `position`");
1670        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1671        assert_eq!(
1672            format!("{node:?}"),
1673            "InlineCode { value: \"a\", position: Some(1:1-1:2 (0-1)), lang: None }",
1674            "should support `position_set`"
1675        );
1676    }
1677
1678    #[test]
1679    fn code() {
1680        let mut node = Node::Code(Code {
1681            value: "a".into(),
1682            position: None,
1683            lang: None,
1684            meta: None,
1685        });
1686
1687        assert_eq!(
1688            format!("{node:?}"),
1689            "Code { value: \"a\", position: None, lang: None, meta: None }",
1690            "should support `Debug`"
1691        );
1692        assert_eq!(node.to_string(), "a", "should support `ToString`");
1693        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1694        assert_eq!(node.children(), None, "should support `children`");
1695        assert_eq!(node.position(), None, "should support `position`");
1696        assert_eq!(node.position_mut(), None, "should support `position`");
1697        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1698        assert_eq!(
1699            format!("{node:?}"),
1700            "Code { value: \"a\", position: Some(1:1-1:2 (0-1)), lang: None, meta: None }",
1701            "should support `position_set`"
1702        );
1703    }
1704
1705    #[test]
1706    fn inline_math() {
1707        let mut node = Node::InlineMath(InlineMath {
1708            value: "a".into(),
1709            position: None,
1710        });
1711
1712        assert_eq!(
1713            format!("{node:?}"),
1714            "InlineMath { value: \"a\", position: None }",
1715            "should support `Debug`"
1716        );
1717        assert_eq!(node.to_string(), "a", "should support `ToString`");
1718        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1719        assert_eq!(node.children(), None, "should support `children`");
1720        assert_eq!(node.position(), None, "should support `position`");
1721        assert_eq!(node.position_mut(), None, "should support `position`");
1722        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1723        assert_eq!(
1724            format!("{node:?}"),
1725            "InlineMath { value: \"a\", position: Some(1:1-1:2 (0-1)) }",
1726            "should support `position_set`"
1727        );
1728    }
1729
1730    #[test]
1731    fn math() {
1732        let mut node = Node::Math(Math {
1733            value: "a".into(),
1734            position: None,
1735            meta: None,
1736        });
1737
1738        assert_eq!(
1739            format!("{node:?}"),
1740            "Math { value: \"a\", position: None, meta: None }",
1741            "should support `Debug`"
1742        );
1743        assert_eq!(node.to_string(), "a", "should support `ToString`");
1744        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1745        assert_eq!(node.children(), None, "should support `children`");
1746        assert_eq!(node.position(), None, "should support `position`");
1747        assert_eq!(node.position_mut(), None, "should support `position`");
1748        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1749        assert_eq!(
1750            format!("{node:?}"),
1751            "Math { value: \"a\", position: Some(1:1-1:2 (0-1)), meta: None }",
1752            "should support `position_set`"
1753        );
1754    }
1755
1756    #[test]
1757    fn html() {
1758        let mut node = Node::Html(Html {
1759            value: "a".into(),
1760            position: None,
1761        });
1762
1763        assert_eq!(
1764            format!("{node:?}"),
1765            "Html { value: \"a\", position: None }",
1766            "should support `Debug`"
1767        );
1768        assert_eq!(node.to_string(), "a", "should support `ToString`");
1769        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1770        assert_eq!(node.children(), None, "should support `children`");
1771        assert_eq!(node.position(), None, "should support `position`");
1772        assert_eq!(node.position_mut(), None, "should support `position`");
1773        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1774        assert_eq!(
1775            format!("{node:?}"),
1776            "Html { value: \"a\", position: Some(1:1-1:2 (0-1)) }",
1777            "should support `position_set`"
1778        );
1779    }
1780
1781    #[test]
1782    fn mdx_text_expression() {
1783        let mut node = Node::MdxTextExpression(MdxTextExpression {
1784            value: "a".into(),
1785            stops: vec![],
1786            position: None,
1787        });
1788
1789        assert_eq!(
1790            format!("{node:?}"),
1791            "MdxTextExpression { value: \"a\", position: None, stops: [] }",
1792            "should support `Debug`"
1793        );
1794        assert_eq!(node.to_string(), "a", "should support `ToString`");
1795        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1796        assert_eq!(node.children(), None, "should support `children`");
1797        assert_eq!(node.position(), None, "should support `position`");
1798        assert_eq!(node.position_mut(), None, "should support `position`");
1799        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1800        assert_eq!(
1801            format!("{node:?}"),
1802            "MdxTextExpression { value: \"a\", position: Some(1:1-1:2 (0-1)), stops: [] }",
1803            "should support `position_set`"
1804        );
1805    }
1806
1807    #[test]
1808    fn mdx_flow_expression() {
1809        let mut node = Node::MdxFlowExpression(MdxFlowExpression {
1810            value: "a".into(),
1811            stops: vec![],
1812            position: None,
1813        });
1814
1815        assert_eq!(
1816            format!("{node:?}"),
1817            "MdxFlowExpression { value: \"a\", position: None, stops: [] }",
1818            "should support `Debug`"
1819        );
1820        assert_eq!(node.to_string(), "a", "should support `ToString`");
1821        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1822        assert_eq!(node.children(), None, "should support `children`");
1823        assert_eq!(node.position(), None, "should support `position`");
1824        assert_eq!(node.position_mut(), None, "should support `position`");
1825        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1826        assert_eq!(
1827            format!("{node:?}"),
1828            "MdxFlowExpression { value: \"a\", position: Some(1:1-1:2 (0-1)), stops: [] }",
1829            "should support `position_set`"
1830        );
1831    }
1832
1833    #[test]
1834    fn mdxjs_esm() {
1835        let mut node = Node::MdxjsEsm(MdxjsEsm {
1836            value: "a".into(),
1837            stops: vec![],
1838            position: None,
1839        });
1840
1841        assert_eq!(
1842            format!("{node:?}"),
1843            "MdxjsEsm { value: \"a\", position: None, stops: [] }",
1844            "should support `Debug`"
1845        );
1846        assert_eq!(node.to_string(), "a", "should support `ToString`");
1847        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1848        assert_eq!(node.children(), None, "should support `children`");
1849        assert_eq!(node.position(), None, "should support `position`");
1850        assert_eq!(node.position_mut(), None, "should support `position`");
1851        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1852        assert_eq!(
1853            format!("{node:?}"),
1854            "MdxjsEsm { value: \"a\", position: Some(1:1-1:2 (0-1)), stops: [] }",
1855            "should support `position_set`"
1856        );
1857    }
1858
1859    #[test]
1860    fn toml() {
1861        let mut node = Node::Toml(Toml {
1862            value: "a".into(),
1863            position: None,
1864        });
1865
1866        assert_eq!(
1867            format!("{node:?}"),
1868            "Toml { value: \"a\", position: None }",
1869            "should support `Debug`"
1870        );
1871        assert_eq!(node.to_string(), "a", "should support `ToString`");
1872        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1873        assert_eq!(node.children(), None, "should support `children`");
1874        assert_eq!(node.position(), None, "should support `position`");
1875        assert_eq!(node.position_mut(), None, "should support `position`");
1876        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1877        assert_eq!(
1878            format!("{node:?}"),
1879            "Toml { value: \"a\", position: Some(1:1-1:2 (0-1)) }",
1880            "should support `position_set`"
1881        );
1882    }
1883
1884    #[test]
1885    fn yaml() {
1886        let mut node = Node::Yaml(Yaml {
1887            value: "a".into(),
1888            position: None,
1889        });
1890
1891        assert_eq!(
1892            format!("{node:?}"),
1893            "Yaml { value: \"a\", position: None }",
1894            "should support `Debug`"
1895        );
1896        assert_eq!(node.to_string(), "a", "should support `ToString`");
1897        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1898        assert_eq!(node.children(), None, "should support `children`");
1899        assert_eq!(node.position(), None, "should support `position`");
1900        assert_eq!(node.position_mut(), None, "should support `position`");
1901        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1902        assert_eq!(
1903            format!("{node:?}"),
1904            "Yaml { value: \"a\", position: Some(1:1-1:2 (0-1)) }",
1905            "should support `position_set`"
1906        );
1907    }
1908
1909    // Voids.
1910
1911    #[test]
1912    fn break_node() {
1913        let mut node = Node::Break(Break { position: None });
1914
1915        assert_eq!(
1916            format!("{node:?}"),
1917            "Break { position: None }",
1918            "should support `Debug`"
1919        );
1920        assert_eq!(node.to_string(), "", "should support `ToString`");
1921        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1922        assert_eq!(node.children(), None, "should support `children`");
1923        assert_eq!(node.position(), None, "should support `position`");
1924        assert_eq!(node.position_mut(), None, "should support `position`");
1925        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1926        assert_eq!(
1927            format!("{node:?}"),
1928            "Break { position: Some(1:1-1:2 (0-1)) }",
1929            "should support `position_set`"
1930        );
1931    }
1932
1933    #[test]
1934    fn thematic_break() {
1935        let mut node = Node::ThematicBreak(ThematicBreak { position: None });
1936
1937        assert_eq!(
1938            format!("{node:?}"),
1939            "ThematicBreak { position: None }",
1940            "should support `Debug`"
1941        );
1942        assert_eq!(node.to_string(), "", "should support `ToString`");
1943        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1944        assert_eq!(node.children(), None, "should support `children`");
1945        assert_eq!(node.position(), None, "should support `position`");
1946        assert_eq!(node.position_mut(), None, "should support `position`");
1947        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1948        assert_eq!(
1949            format!("{node:?}"),
1950            "ThematicBreak { position: Some(1:1-1:2 (0-1)) }",
1951            "should support `position_set`"
1952        );
1953    }
1954
1955    #[test]
1956    fn footnote_reference() {
1957        let mut node = Node::FootnoteReference(FootnoteReference {
1958            position: None,
1959            identifier: "a".into(),
1960            label: Some("b".into()),
1961        });
1962
1963        assert_eq!(
1964            format!("{node:?}"),
1965            "FootnoteReference { position: None, identifier: \"a\", label: Some(\"b\") }",
1966            "should support `Debug`"
1967        );
1968        assert_eq!(node.to_string(), "", "should support `ToString`");
1969        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1970        assert_eq!(node.children(), None, "should support `children`");
1971        assert_eq!(node.position(), None, "should support `position`");
1972        assert_eq!(node.position_mut(), None, "should support `position`");
1973        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
1974        assert_eq!(
1975            format!("{node:?}"),
1976            "FootnoteReference { position: Some(1:1-1:2 (0-1)), identifier: \"a\", label: Some(\"b\") }",
1977            "should support `position_set`"
1978        );
1979    }
1980
1981    #[test]
1982    fn image_reference() {
1983        let mut node = Node::ImageReference(ImageReference {
1984            position: None,
1985            alt: "a".into(),
1986            identifier: "b".into(),
1987            label: Some("c".into()),
1988            reference_kind: ReferenceKind::Full,
1989        });
1990
1991        assert_eq!(
1992            format!("{node:?}"),
1993            "ImageReference { position: None, alt: \"a\", reference_kind: Full, identifier: \"b\", label: Some(\"c\") }",
1994            "should support `Debug`"
1995        );
1996        assert_eq!(node.to_string(), "", "should support `ToString`");
1997        assert_eq!(node.children_mut(), None, "should support `children_mut`");
1998        assert_eq!(node.children(), None, "should support `children`");
1999        assert_eq!(node.position(), None, "should support `position`");
2000        assert_eq!(node.position_mut(), None, "should support `position`");
2001        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2002        assert_eq!(
2003            format!("{node:?}"),
2004            "ImageReference { position: Some(1:1-1:2 (0-1)), alt: \"a\", reference_kind: Full, identifier: \"b\", label: Some(\"c\") }",
2005            "should support `position_set`"
2006        );
2007    }
2008
2009    #[test]
2010    fn image() {
2011        let mut node = Node::Image(Image {
2012            position: None,
2013            alt: "a".into(),
2014            url: "b".into(),
2015            title: None,
2016        });
2017
2018        assert_eq!(
2019            format!("{node:?}"),
2020            "Image { position: None, alt: \"a\", url: \"b\", title: None }",
2021            "should support `Debug`"
2022        );
2023        assert_eq!(node.to_string(), "", "should support `ToString`");
2024        assert_eq!(node.children_mut(), None, "should support `children_mut`");
2025        assert_eq!(node.children(), None, "should support `children`");
2026        assert_eq!(node.position(), None, "should support `position`");
2027        assert_eq!(node.position_mut(), None, "should support `position`");
2028        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2029        assert_eq!(
2030            format!("{node:?}"),
2031            "Image { position: Some(1:1-1:2 (0-1)), alt: \"a\", url: \"b\", title: None }",
2032            "should support `position_set`"
2033        );
2034    }
2035
2036    #[test]
2037    fn definition() {
2038        let mut node = Node::Definition(Definition {
2039            position: None,
2040            identifier: "a".into(),
2041            label: None,
2042            url: "b".into(),
2043            title: None,
2044        });
2045
2046        assert_eq!(
2047            format!("{node:?}"),
2048            "Definition { position: None, url: \"b\", title: None, identifier: \"a\", label: None }",
2049            "should support `Debug`"
2050        );
2051        assert_eq!(node.to_string(), "", "should support `ToString`");
2052        assert_eq!(node.children_mut(), None, "should support `children_mut`");
2053        assert_eq!(node.children(), None, "should support `children`");
2054        assert_eq!(node.position(), None, "should support `position`");
2055        assert_eq!(node.position_mut(), None, "should support `position`");
2056        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2057        assert_eq!(
2058            format!("{node:?}"),
2059            "Definition { position: Some(1:1-1:2 (0-1)), url: \"b\", title: None, identifier: \"a\", label: None }",
2060            "should support `position_set`"
2061        );
2062    }
2063
2064    // Parents.
2065
2066    #[test]
2067    fn root() {
2068        let mut node = Node::Root(Root {
2069            position: None,
2070            children: vec![],
2071        });
2072
2073        assert_eq!(
2074            format!("{node:?}"),
2075            "Root { children: [], position: None }",
2076            "should support `Debug`"
2077        );
2078        assert_eq!(node.to_string(), "", "should support `ToString`");
2079        assert_eq!(
2080            node.children_mut(),
2081            Some(&mut vec![]),
2082            "should support `children_mut`"
2083        );
2084        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2085        assert_eq!(node.position(), None, "should support `position`");
2086        assert_eq!(node.position_mut(), None, "should support `position`");
2087        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2088        assert_eq!(
2089            format!("{node:?}"),
2090            "Root { children: [], position: Some(1:1-1:2 (0-1)) }",
2091            "should support `position_set`"
2092        );
2093    }
2094
2095    #[test]
2096    fn block_quote() {
2097        let mut node = Node::Blockquote(Blockquote {
2098            position: None,
2099            children: vec![],
2100        });
2101
2102        assert_eq!(
2103            format!("{node:?}"),
2104            "Blockquote { children: [], position: None }",
2105            "should support `Debug`"
2106        );
2107        assert_eq!(node.to_string(), "", "should support `ToString`");
2108        assert_eq!(
2109            node.children_mut(),
2110            Some(&mut vec![]),
2111            "should support `children_mut`"
2112        );
2113        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2114        assert_eq!(node.position(), None, "should support `position`");
2115        assert_eq!(node.position_mut(), None, "should support `position`");
2116        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2117        assert_eq!(
2118            format!("{node:?}"),
2119            "Blockquote { children: [], position: Some(1:1-1:2 (0-1)) }",
2120            "should support `position_set`"
2121        );
2122    }
2123
2124    #[test]
2125    fn delete() {
2126        let mut node = Node::Delete(Delete {
2127            position: None,
2128            children: vec![],
2129        });
2130
2131        assert_eq!(
2132            format!("{node:?}"),
2133            "Delete { children: [], position: None }",
2134            "should support `Debug`"
2135        );
2136        assert_eq!(node.to_string(), "", "should support `ToString`");
2137        assert_eq!(
2138            node.children_mut(),
2139            Some(&mut vec![]),
2140            "should support `children_mut`"
2141        );
2142        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2143        assert_eq!(node.position(), None, "should support `position`");
2144        assert_eq!(node.position_mut(), None, "should support `position`");
2145        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2146        assert_eq!(
2147            format!("{node:?}"),
2148            "Delete { children: [], position: Some(1:1-1:2 (0-1)) }",
2149            "should support `position_set`"
2150        );
2151    }
2152
2153    #[test]
2154    fn emphasis() {
2155        let mut node = Node::Emphasis(Emphasis {
2156            position: None,
2157            children: vec![],
2158        });
2159
2160        assert_eq!(
2161            format!("{node:?}"),
2162            "Emphasis { children: [], position: None }",
2163            "should support `Debug`"
2164        );
2165        assert_eq!(node.to_string(), "", "should support `ToString`");
2166        assert_eq!(
2167            node.children_mut(),
2168            Some(&mut vec![]),
2169            "should support `children_mut`"
2170        );
2171        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2172        assert_eq!(node.position(), None, "should support `position`");
2173        assert_eq!(node.position_mut(), None, "should support `position`");
2174        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2175        assert_eq!(
2176            format!("{node:?}"),
2177            "Emphasis { children: [], position: Some(1:1-1:2 (0-1)) }",
2178            "should support `position_set`"
2179        );
2180    }
2181
2182    #[test]
2183    fn strong() {
2184        let mut node = Node::Strong(Strong {
2185            position: None,
2186            children: vec![],
2187        });
2188
2189        assert_eq!(
2190            format!("{node:?}"),
2191            "Strong { children: [], position: None }",
2192            "should support `Debug`"
2193        );
2194        assert_eq!(node.to_string(), "", "should support `ToString`");
2195        assert_eq!(
2196            node.children_mut(),
2197            Some(&mut vec![]),
2198            "should support `children_mut`"
2199        );
2200        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2201        assert_eq!(node.position(), None, "should support `position`");
2202        assert_eq!(node.position_mut(), None, "should support `position`");
2203        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2204        assert_eq!(
2205            format!("{node:?}"),
2206            "Strong { children: [], position: Some(1:1-1:2 (0-1)) }",
2207            "should support `position_set`"
2208        );
2209    }
2210
2211    #[test]
2212    fn paragraph() {
2213        let mut node = Node::Paragraph(Paragraph {
2214            position: None,
2215            children: vec![],
2216        });
2217
2218        assert_eq!(
2219            format!("{node:?}"),
2220            "Paragraph { children: [], position: None }",
2221            "should support `Debug`"
2222        );
2223        assert_eq!(node.to_string(), "", "should support `ToString`");
2224        assert_eq!(
2225            node.children_mut(),
2226            Some(&mut vec![]),
2227            "should support `children_mut`"
2228        );
2229        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2230        assert_eq!(node.position(), None, "should support `position`");
2231        assert_eq!(node.position_mut(), None, "should support `position`");
2232        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2233        assert_eq!(
2234            format!("{node:?}"),
2235            "Paragraph { children: [], position: Some(1:1-1:2 (0-1)) }",
2236            "should support `position_set`"
2237        );
2238    }
2239
2240    #[test]
2241    fn table_row() {
2242        let mut node = Node::TableRow(TableRow {
2243            position: None,
2244            children: vec![],
2245        });
2246
2247        assert_eq!(
2248            format!("{node:?}"),
2249            "TableRow { children: [], position: None }",
2250            "should support `Debug`"
2251        );
2252        assert_eq!(node.to_string(), "", "should support `ToString`");
2253        assert_eq!(
2254            node.children_mut(),
2255            Some(&mut vec![]),
2256            "should support `children_mut`"
2257        );
2258        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2259        assert_eq!(node.position(), None, "should support `position`");
2260        assert_eq!(node.position_mut(), None, "should support `position`");
2261        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2262        assert_eq!(
2263            format!("{node:?}"),
2264            "TableRow { children: [], position: Some(1:1-1:2 (0-1)) }",
2265            "should support `position_set`"
2266        );
2267    }
2268
2269    #[test]
2270    fn table_cell() {
2271        let mut node = Node::TableCell(TableCell {
2272            position: None,
2273            children: vec![],
2274        });
2275
2276        assert_eq!(
2277            format!("{node:?}"),
2278            "TableCell { children: [], position: None }",
2279            "should support `Debug`"
2280        );
2281        assert_eq!(node.to_string(), "", "should support `ToString`");
2282        assert_eq!(
2283            node.children_mut(),
2284            Some(&mut vec![]),
2285            "should support `children_mut`"
2286        );
2287        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2288        assert_eq!(node.position(), None, "should support `position`");
2289        assert_eq!(node.position_mut(), None, "should support `position`");
2290        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2291        assert_eq!(
2292            format!("{node:?}"),
2293            "TableCell { children: [], position: Some(1:1-1:2 (0-1)) }",
2294            "should support `position_set`"
2295        );
2296    }
2297
2298    #[test]
2299    fn heading() {
2300        let mut node = Node::Heading(Heading {
2301            position: None,
2302            depth: 1,
2303            children: vec![],
2304        });
2305
2306        assert_eq!(
2307            format!("{node:?}"),
2308            "Heading { children: [], position: None, depth: 1 }",
2309            "should support `Debug`"
2310        );
2311        assert_eq!(node.to_string(), "", "should support `ToString`");
2312        assert_eq!(
2313            node.children_mut(),
2314            Some(&mut vec![]),
2315            "should support `children_mut`"
2316        );
2317        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2318        assert_eq!(node.position(), None, "should support `position`");
2319        assert_eq!(node.position_mut(), None, "should support `position`");
2320        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2321        assert_eq!(
2322            format!("{node:?}"),
2323            "Heading { children: [], position: Some(1:1-1:2 (0-1)), depth: 1 }",
2324            "should support `position_set`"
2325        );
2326    }
2327
2328    #[test]
2329    fn table() {
2330        let mut node = Node::Table(Table {
2331            position: None,
2332            align: vec![],
2333            children: vec![],
2334        });
2335
2336        assert_eq!(
2337            format!("{node:?}"),
2338            "Table { children: [], position: None, align: [] }",
2339            "should support `Debug`"
2340        );
2341        assert_eq!(node.to_string(), "", "should support `ToString`");
2342        assert_eq!(
2343            node.children_mut(),
2344            Some(&mut vec![]),
2345            "should support `children_mut`"
2346        );
2347        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2348        assert_eq!(node.position(), None, "should support `position`");
2349        assert_eq!(node.position_mut(), None, "should support `position`");
2350        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2351        assert_eq!(
2352            format!("{node:?}"),
2353            "Table { children: [], position: Some(1:1-1:2 (0-1)), align: [] }",
2354            "should support `position_set`"
2355        );
2356    }
2357
2358    #[test]
2359    fn list_item() {
2360        let mut node = Node::ListItem(ListItem {
2361            position: None,
2362            spread: false,
2363            checked: None,
2364            children: vec![],
2365        });
2366
2367        assert_eq!(
2368            format!("{node:?}"),
2369            "ListItem { children: [], position: None, spread: false, checked: None }",
2370            "should support `Debug`"
2371        );
2372        assert_eq!(node.to_string(), "", "should support `ToString`");
2373        assert_eq!(
2374            node.children_mut(),
2375            Some(&mut vec![]),
2376            "should support `children_mut`"
2377        );
2378        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2379        assert_eq!(node.position(), None, "should support `position`");
2380        assert_eq!(node.position_mut(), None, "should support `position`");
2381        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2382        assert_eq!(
2383            format!("{node:?}"),
2384            "ListItem { children: [], position: Some(1:1-1:2 (0-1)), spread: false, checked: None }",
2385            "should support `position_set`"
2386        );
2387    }
2388
2389    #[test]
2390    fn list() {
2391        let mut node = Node::List(List {
2392            position: None,
2393            spread: false,
2394            ordered: false,
2395            start: None,
2396            children: vec![],
2397        });
2398
2399        assert_eq!(
2400            format!("{node:?}"),
2401            "List { children: [], position: None, ordered: false, start: None, spread: false }",
2402            "should support `Debug`"
2403        );
2404        assert_eq!(node.to_string(), "", "should support `ToString`");
2405        assert_eq!(
2406            node.children_mut(),
2407            Some(&mut vec![]),
2408            "should support `children_mut`"
2409        );
2410        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2411        assert_eq!(node.position(), None, "should support `position`");
2412        assert_eq!(node.position_mut(), None, "should support `position`");
2413        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2414        assert_eq!(
2415            format!("{node:?}"),
2416            "List { children: [], position: Some(1:1-1:2 (0-1)), ordered: false, start: None, spread: false }",
2417            "should support `position_set`"
2418        );
2419    }
2420
2421    #[test]
2422    fn link_reference() {
2423        let mut node = Node::LinkReference(LinkReference {
2424            position: None,
2425            identifier: "a".into(),
2426            label: None,
2427            reference_kind: ReferenceKind::Full,
2428            children: vec![],
2429        });
2430
2431        assert_eq!(
2432            format!("{node:?}"),
2433            "LinkReference { children: [], position: None, reference_kind: Full, identifier: \"a\", label: None }",
2434            "should support `Debug`"
2435        );
2436        assert_eq!(node.to_string(), "", "should support `ToString`");
2437        assert_eq!(
2438            node.children_mut(),
2439            Some(&mut vec![]),
2440            "should support `children_mut`"
2441        );
2442        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2443        assert_eq!(node.position(), None, "should support `position`");
2444        assert_eq!(node.position_mut(), None, "should support `position`");
2445        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2446        assert_eq!(
2447            format!("{node:?}"),
2448            "LinkReference { children: [], position: Some(1:1-1:2 (0-1)), reference_kind: Full, identifier: \"a\", label: None }",
2449            "should support `position_set`"
2450        );
2451    }
2452
2453    #[test]
2454    fn link() {
2455        let mut node = Node::Link(Link {
2456            position: None,
2457            url: "a".into(),
2458            title: None,
2459            children: vec![],
2460        });
2461
2462        assert_eq!(
2463            format!("{node:?}"),
2464            "Link { children: [], position: None, url: \"a\", title: None }",
2465            "should support `Debug`"
2466        );
2467        assert_eq!(node.to_string(), "", "should support `ToString`");
2468        assert_eq!(
2469            node.children_mut(),
2470            Some(&mut vec![]),
2471            "should support `children_mut`"
2472        );
2473        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2474        assert_eq!(node.position(), None, "should support `position`");
2475        assert_eq!(node.position_mut(), None, "should support `position`");
2476        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2477        assert_eq!(
2478            format!("{node:?}"),
2479            "Link { children: [], position: Some(1:1-1:2 (0-1)), url: \"a\", title: None }",
2480            "should support `position_set`"
2481        );
2482    }
2483
2484    #[test]
2485    fn footnote_definition() {
2486        let mut node = Node::FootnoteDefinition(FootnoteDefinition {
2487            position: None,
2488            identifier: "a".into(),
2489            label: None,
2490            children: vec![],
2491        });
2492
2493        assert_eq!(
2494            format!("{node:?}"),
2495            "FootnoteDefinition { children: [], position: None, identifier: \"a\", label: None }",
2496            "should support `Debug`"
2497        );
2498        assert_eq!(node.to_string(), "", "should support `ToString`");
2499        assert_eq!(
2500            node.children_mut(),
2501            Some(&mut vec![]),
2502            "should support `children_mut`"
2503        );
2504        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2505        assert_eq!(node.position(), None, "should support `position`");
2506        assert_eq!(node.position_mut(), None, "should support `position`");
2507        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2508        assert_eq!(
2509            format!("{node:?}"),
2510            "FootnoteDefinition { children: [], position: Some(1:1-1:2 (0-1)), identifier: \"a\", label: None }",
2511            "should support `position_set`"
2512        );
2513    }
2514
2515    #[test]
2516    fn mdx_jsx_flow_element() {
2517        let mut node = Node::MdxJsxFlowElement(MdxJsxFlowElement {
2518            position: None,
2519            name: None,
2520            attributes: vec![],
2521            children: vec![],
2522        });
2523
2524        assert_eq!(
2525            format!("{node:?}"),
2526            "MdxJsxFlowElement { children: [], position: None, name: None, attributes: [] }",
2527            "should support `Debug`"
2528        );
2529        assert_eq!(node.to_string(), "", "should support `ToString`");
2530        assert_eq!(
2531            node.children_mut(),
2532            Some(&mut vec![]),
2533            "should support `children_mut`"
2534        );
2535        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2536        assert_eq!(node.position(), None, "should support `position`");
2537        assert_eq!(node.position_mut(), None, "should support `position`");
2538        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2539        assert_eq!(
2540            format!("{node:?}"),
2541            "MdxJsxFlowElement { children: [], position: Some(1:1-1:2 (0-1)), name: None, attributes: [] }",
2542            "should support `position_set`"
2543        );
2544    }
2545
2546    #[test]
2547    fn mdx_jsx_text_element() {
2548        let mut node = Node::MdxJsxTextElement(MdxJsxTextElement {
2549            position: None,
2550            name: None,
2551            attributes: vec![],
2552            children: vec![],
2553        });
2554
2555        assert_eq!(
2556            format!("{node:?}"),
2557            "MdxJsxTextElement { children: [], position: None, name: None, attributes: [] }",
2558            "should support `Debug`"
2559        );
2560        assert_eq!(node.to_string(), "", "should support `ToString`");
2561        assert_eq!(
2562            node.children_mut(),
2563            Some(&mut vec![]),
2564            "should support `children_mut`"
2565        );
2566        assert_eq!(node.children(), Some(&vec![]), "should support `children`");
2567        assert_eq!(node.position(), None, "should support `position`");
2568        assert_eq!(node.position_mut(), None, "should support `position`");
2569        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2570        assert_eq!(
2571            format!("{node:?}"),
2572            "MdxJsxTextElement { children: [], position: Some(1:1-1:2 (0-1)), name: None, attributes: [] }",
2573            "should support `position_set`"
2574        );
2575    }
2576
2577    #[test]
2578    fn fragment_node_methods() {
2579        let text = Node::Text(Text {
2580            value: "fragment".into(),
2581            position: None,
2582        });
2583        let mut node = Node::Fragment(Fragment {
2584            children: vec![text],
2585        });
2586
2587        assert_eq!(
2588            format!("{node:?}"),
2589            "Fragment { children: [Text { value: \"fragment\", position: None }] }"
2590        );
2591        assert_eq!(node.to_string(), "fragment");
2592        assert_eq!(node.children().map(Vec::len), Some(1));
2593        assert_eq!(node.position(), None);
2594        assert_eq!(node.position_mut(), None);
2595        node.position_set(Some(Position::new(1, 1, 0, 1, 2, 1)));
2596        assert_eq!(node.position(), None);
2597
2598        let extracted = node.extract_children().expect("extract fragment children");
2599        assert_eq!(extracted.to_string(), "fragment");
2600        assert_eq!(node.children().unwrap(), &[]);
2601        assert!(node.extract_children().is_some());
2602    }
2603
2604    #[test]
2605    fn walks_and_mutates_nested_children() {
2606        let mut node = Node::Root(Root {
2607            position: None,
2608            children: vec![Node::Paragraph(Paragraph {
2609                position: None,
2610                children: vec![Node::Text(Text {
2611                    value: "before".into(),
2612                    position: None,
2613                })],
2614            })],
2615        });
2616
2617        let mut visited = Vec::new();
2618        node.walk(|node| visited.push(node.to_string()));
2619        assert_eq!(visited, ["before", "before", "before"]);
2620
2621        node.walk_mut(|node| {
2622            if let Node::Text(text) = node {
2623                text.value.push_str(" after");
2624            }
2625        });
2626        assert_eq!(node.text_content(), "before after");
2627    }
2628
2629    #[test]
2630    fn node_text_and_heading_helpers() {
2631        let image = Node::Image(Image {
2632            position: None,
2633            alt: "image alt".into(),
2634            url: "image.png".into(),
2635            title: None,
2636        });
2637        let image_reference = Node::ImageReference(ImageReference {
2638            position: None,
2639            alt: "reference alt".into(),
2640            reference_kind: ReferenceKind::Full,
2641            identifier: "image".into(),
2642            label: None,
2643        });
2644        let heading = Node::Heading(Heading {
2645            position: None,
2646            depth: 2,
2647            children: vec![Node::Text(Text {
2648                value: "Target".into(),
2649                position: None,
2650            })],
2651        });
2652        let root = Node::Root(Root {
2653            position: None,
2654            children: vec![image.clone(), image_reference.clone(), heading.clone()],
2655        });
2656
2657        assert_eq!(image.text_content(), "image alt");
2658        assert_eq!(image_reference.text_content(), "reference alt");
2659        assert_eq!(Node::Break(Break { position: None }).text_content(), "\n");
2660        assert_eq!(
2661            Node::ThematicBreak(ThematicBreak { position: None }).text_content(),
2662            ""
2663        );
2664        assert_eq!(
2665            Node::Definition(Definition {
2666                position: None,
2667                identifier: "x".into(),
2668                label: None,
2669                url: "x".into(),
2670                title: None
2671            })
2672            .text_content(),
2673            ""
2674        );
2675        assert_eq!(root.find_heading("Target"), Some(&heading));
2676        assert_eq!(root.find_heading("missing"), None);
2677        assert_eq!(image.find_heading("Target"), None);
2678        assert_eq!(
2679            Node::Text(Text {
2680                value: "x".into(),
2681                position: None
2682            })
2683            .code_language(),
2684            None
2685        );
2686    }
2687
2688    #[test]
2689    fn code_accessors_cover_optional_metadata() {
2690        let inline = Node::InlineCode(InlineCode {
2691            value: "code".into(),
2692            position: None,
2693            lang: Some("rust".into()),
2694        });
2695        assert_eq!(inline.code_language(), Some("rust"));
2696        assert_eq!(inline.code_info().as_deref(), Some("rust"));
2697
2698        for (lang, meta, expected) in [
2699            (Some("rust"), Some("ignore"), Some("rust ignore")),
2700            (Some("rust"), None, Some("rust")),
2701            (None, Some("ignore"), Some("ignore")),
2702            (None, None, None),
2703        ] {
2704            let code = Node::Code(Code {
2705                value: "code".into(),
2706                position: None,
2707                lang: lang.map(Into::into),
2708                meta: meta.map(Into::into),
2709            });
2710            assert_eq!(code.code_language(), lang);
2711            assert_eq!(code.code_info().as_deref(), expected);
2712        }
2713
2714        assert_eq!(
2715            Node::Text(Text {
2716                value: "plain".into(),
2717                position: None,
2718            })
2719            .code_info(),
2720            None
2721        );
2722    }
2723
2724    #[test]
2725    fn node_serializes_markdown_with_custom_options() {
2726        let node = Node::Text(Text {
2727            value: "text".into(),
2728            position: None,
2729        });
2730        let options = crate::markdown::Options::default();
2731
2732        assert_eq!(node.to_markdown_with_options(&options).unwrap(), "text\n");
2733    }
2734
2735    #[test]
2736    fn replaces_heading_sections_with_all_options() {
2737        let heading = |depth, title: &str| {
2738            Node::Heading(Heading {
2739                position: None,
2740                depth,
2741                children: vec![Node::Text(Text {
2742                    value: title.into(),
2743                    position: None,
2744                })],
2745            })
2746        };
2747        let text = |value: &str| {
2748            Node::Text(Text {
2749                value: value.into(),
2750                position: None,
2751            })
2752        };
2753        let replacement = || vec![text("replacement")];
2754
2755        let mut root = Node::Root(Root {
2756            position: None,
2757            children: vec![
2758                heading(1, "one"),
2759                text("body"),
2760                heading(2, "nested"),
2761                text("nested body"),
2762                heading(1, "two"),
2763            ],
2764        });
2765        assert!(!root.replace_heading_section(1, replacement(), true, false));
2766        assert!(!text("not a parent").replace_heading_section(0, replacement(), true, false));
2767        assert!(root.replace_heading_section(0, replacement(), false, true));
2768        assert_eq!(root.to_string(), "onereplacementtwo");
2769
2770        let mut root = Node::Root(Root {
2771            position: None,
2772            children: vec![
2773                heading(1, "one"),
2774                text("body"),
2775                heading(2, "nested"),
2776                text("nested body"),
2777                heading(1, "two"),
2778            ],
2779        });
2780        assert!(root.replace_heading_section(0, replacement(), true, false));
2781        assert_eq!(root.to_string(), "replacementnestednested bodytwo");
2782
2783        let mut root = Node::Root(Root {
2784            position: None,
2785            children: vec![
2786                heading(1, "one"),
2787                text("body"),
2788                heading(2, "nested"),
2789                text("nested body"),
2790                heading(1, "two"),
2791            ],
2792        });
2793        assert!(root.replace_heading_section(0, replacement(), true, true));
2794        assert_eq!(root.to_string(), "replacementtwo");
2795    }
2796
2797    #[cfg(feature = "serde")]
2798    #[test]
2799    fn align_kind_serialization_and_visitors() {
2800        use serde::de::Visitor;
2801
2802        assert_eq!(serde_json::to_string(&AlignKind::Left).unwrap(), "\"left\"");
2803        assert_eq!(
2804            serde_json::to_string(&AlignKind::Right).unwrap(),
2805            "\"right\""
2806        );
2807        assert_eq!(
2808            serde_json::to_string(&AlignKind::Center).unwrap(),
2809            "\"center\""
2810        );
2811        assert_eq!(serde_json::to_string(&AlignKind::None).unwrap(), "null");
2812        assert_eq!(
2813            serde_json::from_str::<AlignKind>("\"left\"").unwrap(),
2814            AlignKind::Left
2815        );
2816        assert_eq!(
2817            serde_json::from_str::<AlignKind>("null").unwrap(),
2818            AlignKind::None
2819        );
2820
2821        assert_eq!(
2822            AlignKindVisitor
2823                .visit_bytes::<serde_json::Error>(b"right")
2824                .unwrap(),
2825            AlignKind::Right
2826        );
2827        assert_eq!(
2828            AlignKindVisitor
2829                .visit_bytes::<serde_json::Error>(b"left")
2830                .unwrap(),
2831            AlignKind::Left
2832        );
2833        assert_eq!(
2834            AlignKindVisitor
2835                .visit_bytes::<serde_json::Error>(b"center")
2836                .unwrap(),
2837            AlignKind::Center
2838        );
2839        assert_eq!(
2840            AlignKindVisitor.visit_none::<serde_json::Error>().unwrap(),
2841            AlignKind::None
2842        );
2843        assert_eq!(
2844            AlignKindVisitor.visit_unit::<serde_json::Error>().unwrap(),
2845            AlignKind::None
2846        );
2847        assert!(AlignKindVisitor
2848            .visit_str::<serde_json::Error>("invalid")
2849            .is_err());
2850        assert!(AlignKindVisitor
2851            .visit_bytes::<serde_json::Error>(b"invalid")
2852            .is_err());
2853    }
2854}