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