mq_markdown/
node.rs

1use std::{
2    borrow::Cow,
3    fmt::{self, Display},
4};
5
6use itertools::Itertools;
7use markdown::mdast::{self};
8use smol_str::SmolStr;
9
10type Level = u8;
11
12pub const EMPTY_NODE: Node = Node::Text(Text {
13    value: String::new(),
14    position: None,
15});
16
17#[derive(Debug, Clone, Default, PartialEq)]
18pub struct RenderOptions {
19    pub list_style: ListStyle,
20    pub link_url_style: UrlSurroundStyle,
21    pub link_title_style: TitleSurroundStyle,
22}
23
24#[derive(Debug, Clone, Default, PartialEq)]
25pub enum ListStyle {
26    #[default]
27    Dash,
28    Plus,
29    Star,
30}
31
32impl Display for ListStyle {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        match self {
35            ListStyle::Dash => write!(f, "-"),
36            ListStyle::Plus => write!(f, "+"),
37            ListStyle::Star => write!(f, "*"),
38        }
39    }
40}
41
42#[derive(Debug, Clone, PartialEq, Default)]
43#[cfg_attr(
44    feature = "json",
45    derive(serde::Serialize, serde::Deserialize),
46    serde(rename_all = "camelCase")
47)]
48pub struct Url(String);
49
50#[derive(Debug, Clone, PartialEq, Default)]
51pub enum UrlSurroundStyle {
52    #[default]
53    None,
54    Angle,
55}
56
57impl Url {
58    pub fn new(value: String) -> Self {
59        Self(value)
60    }
61
62    pub fn as_str(&self) -> &str {
63        &self.0
64    }
65
66    pub fn to_string_with(&self, options: &RenderOptions) -> Cow<'_, str> {
67        match options.link_url_style {
68            UrlSurroundStyle::None if self.0.is_empty() => Cow::Borrowed(""),
69            UrlSurroundStyle::None => Cow::Borrowed(&self.0),
70            UrlSurroundStyle::Angle => Cow::Owned(format!("<{}>", self.0)),
71        }
72    }
73}
74
75#[derive(Debug, Clone, PartialEq, Default)]
76pub enum TitleSurroundStyle {
77    #[default]
78    Double,
79    Single,
80    Paren,
81}
82
83#[derive(Debug, Clone, PartialEq)]
84#[cfg_attr(
85    feature = "json",
86    derive(serde::Serialize, serde::Deserialize),
87    serde(rename_all = "camelCase")
88)]
89pub struct Title(String);
90
91impl Display for Title {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        write!(f, "{}", self.0)
94    }
95}
96
97impl Title {
98    pub fn new(value: String) -> Self {
99        Self(value)
100    }
101
102    pub fn to_value(&self) -> String {
103        self.0.clone()
104    }
105
106    pub fn to_string_with(&self, options: &RenderOptions) -> Cow<'_, str> {
107        match options.link_title_style {
108            TitleSurroundStyle::Double => Cow::Owned(format!("\"{}\"", self)),
109            TitleSurroundStyle::Single => Cow::Owned(format!("'{}'", self)),
110            TitleSurroundStyle::Paren => Cow::Owned(format!("({})", self)),
111        }
112    }
113}
114
115#[derive(Debug, Clone, PartialEq)]
116#[cfg_attr(
117    feature = "json",
118    derive(serde::Serialize, serde::Deserialize),
119    serde(rename_all = "camelCase")
120)]
121pub enum TableAlignKind {
122    Left,
123    Right,
124    Center,
125    None,
126}
127
128impl From<mdast::AlignKind> for TableAlignKind {
129    fn from(value: mdast::AlignKind) -> Self {
130        match value {
131            mdast::AlignKind::Left => Self::Left,
132            mdast::AlignKind::Right => Self::Right,
133            mdast::AlignKind::Center => Self::Center,
134            mdast::AlignKind::None => Self::None,
135        }
136    }
137}
138
139impl Display for TableAlignKind {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        match self {
142            TableAlignKind::Left => write!(f, ":---"),
143            TableAlignKind::Right => write!(f, "---:"),
144            TableAlignKind::Center => write!(f, ":---:"),
145            TableAlignKind::None => write!(f, "---"),
146        }
147    }
148}
149
150#[derive(Debug, Clone, PartialEq, Default)]
151#[cfg_attr(
152    feature = "json",
153    derive(serde::Serialize, serde::Deserialize),
154    serde(rename_all = "camelCase", tag = "type")
155)]
156pub struct List {
157    pub values: Vec<Node>,
158    pub index: usize,
159    pub level: Level,
160    pub ordered: bool,
161    pub checked: Option<bool>,
162    pub position: Option<Position>,
163}
164
165#[derive(Debug, Clone, PartialEq)]
166#[cfg_attr(
167    feature = "json",
168    derive(serde::Serialize, serde::Deserialize),
169    serde(rename_all = "camelCase", tag = "type")
170)]
171pub struct TableCell {
172    pub values: Vec<Node>,
173    pub column: usize,
174    pub row: usize,
175    pub last_cell_in_row: bool,
176    pub last_cell_of_in_table: bool,
177    #[cfg_attr(feature = "json", serde(skip))]
178    pub position: Option<Position>,
179}
180
181#[derive(Debug, Clone, PartialEq)]
182#[cfg_attr(
183    feature = "json",
184    derive(serde::Serialize, serde::Deserialize),
185    serde(rename_all = "camelCase", tag = "type")
186)]
187pub struct TableRow {
188    pub values: Vec<Node>,
189    #[cfg_attr(feature = "json", serde(skip))]
190    pub position: Option<Position>,
191}
192
193#[derive(Debug, Clone, PartialEq)]
194#[cfg_attr(
195    feature = "json",
196    derive(serde::Serialize, serde::Deserialize),
197    serde(rename_all = "camelCase", tag = "type")
198)]
199pub struct TableHeader {
200    pub align: Vec<TableAlignKind>,
201    #[cfg_attr(feature = "json", serde(skip))]
202    pub position: Option<Position>,
203}
204
205#[derive(Debug, Clone, PartialEq)]
206#[cfg_attr(
207    feature = "json",
208    derive(serde::Serialize, serde::Deserialize),
209    serde(rename_all = "camelCase", tag = "type")
210)]
211pub struct Fragment {
212    pub values: Vec<Node>,
213}
214
215#[derive(Debug, Clone, PartialEq, Default)]
216#[cfg_attr(
217    feature = "json",
218    derive(serde::Serialize, serde::Deserialize),
219    serde(rename_all = "camelCase", tag = "type")
220)]
221pub struct Code {
222    pub value: String,
223    pub lang: Option<String>,
224    #[cfg_attr(feature = "json", serde(skip))]
225    pub position: Option<Position>,
226    pub meta: Option<String>,
227    pub fence: bool,
228}
229
230#[derive(Debug, Clone, PartialEq)]
231#[cfg_attr(
232    feature = "json",
233    derive(serde::Serialize, serde::Deserialize),
234    serde(rename_all = "camelCase", tag = "type")
235)]
236pub struct Image {
237    pub alt: String,
238    pub url: String,
239    pub title: Option<String>,
240    #[cfg_attr(feature = "json", serde(skip))]
241    pub position: Option<Position>,
242}
243
244#[derive(Debug, Clone, PartialEq, Default)]
245#[cfg_attr(
246    feature = "json",
247    derive(serde::Serialize, serde::Deserialize),
248    serde(rename_all = "camelCase", tag = "type")
249)]
250pub struct ImageRef {
251    pub alt: String,
252    pub ident: String,
253    pub label: Option<String>,
254    #[cfg_attr(feature = "json", serde(skip))]
255    pub position: Option<Position>,
256}
257#[derive(Debug, Clone, PartialEq)]
258#[cfg_attr(
259    feature = "json",
260    derive(serde::Serialize, serde::Deserialize),
261    serde(rename_all = "camelCase", tag = "type")
262)]
263pub struct Link {
264    pub url: Url,
265    pub title: Option<Title>,
266    pub values: Vec<Node>,
267    #[cfg_attr(feature = "json", serde(skip))]
268    pub position: Option<Position>,
269}
270
271#[derive(Debug, Clone, PartialEq, Default)]
272#[cfg_attr(
273    feature = "json",
274    derive(serde::Serialize, serde::Deserialize),
275    serde(rename_all = "camelCase", tag = "type")
276)]
277pub struct FootnoteRef {
278    pub ident: String,
279    pub label: Option<String>,
280    #[cfg_attr(feature = "json", serde(skip))]
281    pub position: Option<Position>,
282}
283
284#[derive(Debug, Clone, PartialEq, Default)]
285#[cfg_attr(
286    feature = "json",
287    derive(serde::Serialize, serde::Deserialize),
288    serde(rename_all = "camelCase", tag = "type")
289)]
290pub struct Footnote {
291    pub ident: String,
292    pub values: Vec<Node>,
293    #[cfg_attr(feature = "json", serde(skip))]
294    pub position: Option<Position>,
295}
296
297#[derive(Debug, Clone, PartialEq, Default)]
298#[cfg_attr(
299    feature = "json",
300    derive(serde::Serialize, serde::Deserialize),
301    serde(rename_all = "camelCase", tag = "type")
302)]
303pub struct LinkRef {
304    pub ident: String,
305    pub label: Option<String>,
306    pub values: Vec<Node>,
307    #[cfg_attr(feature = "json", serde(skip))]
308    pub position: Option<Position>,
309}
310
311#[derive(Debug, Clone, PartialEq, Default)]
312#[cfg_attr(
313    feature = "json",
314    derive(serde::Serialize, serde::Deserialize),
315    serde(rename_all = "camelCase", tag = "type")
316)]
317pub struct Heading {
318    pub depth: u8,
319    pub values: Vec<Node>,
320    #[cfg_attr(feature = "json", serde(skip))]
321    pub position: Option<Position>,
322}
323
324#[derive(Debug, Clone, PartialEq, Default)]
325#[cfg_attr(
326    feature = "json",
327    derive(serde::Serialize, serde::Deserialize),
328    serde(rename_all = "camelCase", tag = "type")
329)]
330pub struct Definition {
331    #[cfg_attr(feature = "json", serde(skip))]
332    pub position: Option<Position>,
333    pub url: Url,
334    pub title: Option<Title>,
335    pub ident: String,
336    pub label: Option<String>,
337}
338#[derive(Debug, Clone, PartialEq)]
339#[cfg_attr(
340    feature = "json",
341    derive(serde::Serialize, serde::Deserialize),
342    serde(rename_all = "camelCase", tag = "type")
343)]
344pub struct Text {
345    pub value: String,
346    #[cfg_attr(feature = "json", serde(skip))]
347    pub position: Option<Position>,
348}
349
350#[derive(Debug, Clone, PartialEq)]
351#[cfg_attr(
352    feature = "json",
353    derive(serde::Serialize, serde::Deserialize),
354    serde(rename_all = "camelCase", tag = "type")
355)]
356pub struct Html {
357    pub value: String,
358    #[cfg_attr(feature = "json", serde(skip))]
359    pub position: Option<Position>,
360}
361
362#[derive(Debug, Clone, PartialEq)]
363#[cfg_attr(
364    feature = "json",
365    derive(serde::Serialize, serde::Deserialize),
366    serde(rename_all = "camelCase", tag = "type")
367)]
368pub struct Toml {
369    pub value: String,
370    #[cfg_attr(feature = "json", serde(skip))]
371    pub position: Option<Position>,
372}
373
374#[derive(Debug, Clone, PartialEq)]
375#[cfg_attr(
376    feature = "json",
377    derive(serde::Serialize, serde::Deserialize),
378    serde(rename_all = "camelCase", tag = "type")
379)]
380pub struct Yaml {
381    pub value: String,
382    #[cfg_attr(feature = "json", serde(skip))]
383    pub position: Option<Position>,
384}
385
386#[derive(Debug, Clone, PartialEq)]
387#[cfg_attr(
388    feature = "json",
389    derive(serde::Serialize, serde::Deserialize),
390    serde(rename_all = "camelCase", tag = "type")
391)]
392pub struct CodeInline {
393    pub value: SmolStr,
394    #[cfg_attr(feature = "json", serde(skip))]
395    pub position: Option<Position>,
396}
397
398#[derive(Debug, Clone, PartialEq)]
399#[cfg_attr(
400    feature = "json",
401    derive(serde::Serialize, serde::Deserialize),
402    serde(rename_all = "camelCase", tag = "type")
403)]
404pub struct MathInline {
405    pub value: SmolStr,
406    #[cfg_attr(feature = "json", serde(skip))]
407    pub position: Option<Position>,
408}
409
410#[derive(Debug, Clone, PartialEq)]
411#[cfg_attr(
412    feature = "json",
413    derive(serde::Serialize, serde::Deserialize),
414    serde(rename_all = "camelCase", tag = "type")
415)]
416pub struct Math {
417    pub value: String,
418    #[cfg_attr(feature = "json", serde(skip))]
419    pub position: Option<Position>,
420}
421
422#[derive(Debug, Clone, PartialEq)]
423#[cfg_attr(
424    feature = "json",
425    derive(serde::Serialize, serde::Deserialize),
426    serde(rename_all = "camelCase", tag = "type")
427)]
428pub struct MdxFlowExpression {
429    pub value: SmolStr,
430    #[cfg_attr(feature = "json", serde(skip))]
431    pub position: Option<Position>,
432}
433
434#[derive(Debug, Clone, PartialEq)]
435#[cfg_attr(
436    feature = "json",
437    derive(serde::Serialize, serde::Deserialize),
438    serde(rename_all = "camelCase", tag = "type")
439)]
440pub struct MdxJsxFlowElement {
441    pub children: Vec<Node>,
442    #[cfg_attr(feature = "json", serde(skip))]
443    pub position: Option<Position>,
444    pub name: Option<String>,
445    pub attributes: Vec<MdxAttributeContent>,
446}
447
448#[derive(Debug, Clone, PartialEq)]
449#[cfg_attr(
450    feature = "json",
451    derive(serde::Serialize, serde::Deserialize),
452    serde(rename_all = "camelCase", tag = "type")
453)]
454pub enum MdxAttributeContent {
455    Expression(SmolStr),
456    Property(MdxJsxAttribute),
457}
458
459#[derive(Debug, Clone, PartialEq)]
460#[cfg_attr(
461    feature = "json",
462    derive(serde::Serialize, serde::Deserialize),
463    serde(rename_all = "camelCase", tag = "type")
464)]
465pub struct MdxJsxAttribute {
466    pub name: SmolStr,
467    pub value: Option<MdxAttributeValue>,
468}
469
470#[derive(Debug, Clone, PartialEq)]
471#[cfg_attr(
472    feature = "json",
473    derive(serde::Serialize, serde::Deserialize),
474    serde(rename_all = "camelCase", tag = "type")
475)]
476pub enum MdxAttributeValue {
477    Expression(SmolStr),
478    Literal(SmolStr),
479}
480
481/// Represents a typed attribute value that can be returned from or passed to attr/set_attr methods.
482#[derive(Debug, Clone, PartialEq)]
483#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize), serde(untagged))]
484pub enum AttrValue {
485    Array(Vec<Node>),
486    String(String),
487    Number(f64),
488    Integer(i64),
489    Boolean(bool),
490    Null,
491}
492
493impl From<String> for AttrValue {
494    fn from(s: String) -> Self {
495        AttrValue::String(s)
496    }
497}
498
499impl From<&str> for AttrValue {
500    fn from(s: &str) -> Self {
501        AttrValue::String(s.to_string())
502    }
503}
504
505impl From<f64> for AttrValue {
506    fn from(n: f64) -> Self {
507        AttrValue::Number(n)
508    }
509}
510
511impl From<i64> for AttrValue {
512    fn from(n: i64) -> Self {
513        AttrValue::Integer(n)
514    }
515}
516
517impl From<i32> for AttrValue {
518    fn from(n: i32) -> Self {
519        AttrValue::Integer(n as i64)
520    }
521}
522
523impl From<usize> for AttrValue {
524    fn from(n: usize) -> Self {
525        AttrValue::Integer(n as i64)
526    }
527}
528
529impl From<u8> for AttrValue {
530    fn from(n: u8) -> Self {
531        AttrValue::Integer(n as i64)
532    }
533}
534
535impl From<bool> for AttrValue {
536    fn from(b: bool) -> Self {
537        AttrValue::Boolean(b)
538    }
539}
540
541impl AttrValue {
542    /// Converts the attribute value to a string representation.
543    pub fn as_string(&self) -> String {
544        match self {
545            AttrValue::String(s) => s.clone(),
546            AttrValue::Number(n) => n.to_string(),
547            AttrValue::Integer(i) => i.to_string(),
548            AttrValue::Boolean(b) => b.to_string(),
549            AttrValue::Array(arr) => values_to_string(arr, &RenderOptions::default()),
550            AttrValue::Null => String::new(),
551        }
552    }
553
554    /// Converts the attribute value to an integer representation.
555    pub fn as_i64(&self) -> Option<i64> {
556        match self {
557            AttrValue::String(s) => s.parse().ok(),
558            AttrValue::Number(n) => Some(*n as i64),
559            AttrValue::Integer(i) => Some(*i),
560            AttrValue::Boolean(b) => Some(*b as i64),
561            AttrValue::Array(arr) => Some(arr.len() as i64),
562            AttrValue::Null => None,
563        }
564    }
565
566    /// Converts the attribute value to a number representation.
567    pub fn as_f64(&self) -> Option<f64> {
568        match self {
569            AttrValue::String(s) => s.parse().ok(),
570            AttrValue::Number(n) => Some(*n),
571            AttrValue::Integer(i) => Some(*i as f64),
572            AttrValue::Boolean(_) => None,
573            AttrValue::Array(arr) => Some(arr.len() as f64),
574            AttrValue::Null => None,
575        }
576    }
577
578    /// Returns `true` if the attribute value is a string.
579    pub fn is_string(&self) -> bool {
580        matches!(self, AttrValue::String(_))
581    }
582
583    /// Returns `true` if the attribute value is a number.
584    pub fn is_number(&self) -> bool {
585        matches!(self, AttrValue::Number(_))
586    }
587
588    /// Returns `true` if the attribute value is an integer.
589    pub fn is_integer(&self) -> bool {
590        matches!(self, AttrValue::Integer(_))
591    }
592
593    /// Returns `true` if the attribute value is a boolean.
594    pub fn is_boolean(&self) -> bool {
595        matches!(self, AttrValue::Boolean(_))
596    }
597
598    /// Returns `true` if the attribute value is null.
599    pub fn is_null(&self) -> bool {
600        matches!(self, AttrValue::Null)
601    }
602}
603
604#[derive(Debug, Clone, PartialEq)]
605#[cfg_attr(
606    feature = "json",
607    derive(serde::Serialize, serde::Deserialize),
608    serde(rename_all = "camelCase", tag = "type")
609)]
610pub struct MdxJsxTextElement {
611    pub children: Vec<Node>,
612    #[cfg_attr(feature = "json", serde(skip))]
613    pub position: Option<Position>,
614    pub name: Option<SmolStr>,
615    pub attributes: Vec<MdxAttributeContent>,
616}
617
618#[derive(Debug, Clone, PartialEq)]
619#[cfg_attr(
620    feature = "json",
621    derive(serde::Serialize, serde::Deserialize),
622    serde(rename_all = "camelCase", tag = "type")
623)]
624pub struct MdxTextExpression {
625    pub value: SmolStr,
626    #[cfg_attr(feature = "json", serde(skip))]
627    pub position: Option<Position>,
628}
629
630#[derive(Debug, Clone, PartialEq)]
631#[cfg_attr(
632    feature = "json",
633    derive(serde::Serialize, serde::Deserialize),
634    serde(rename_all = "camelCase", tag = "type")
635)]
636pub struct MdxJsEsm {
637    pub value: SmolStr,
638    #[cfg_attr(feature = "json", serde(skip))]
639    pub position: Option<Position>,
640}
641
642#[derive(Debug, Clone, PartialEq)]
643#[cfg_attr(
644    feature = "json",
645    derive(serde::Serialize, serde::Deserialize),
646    serde(rename_all = "camelCase", tag = "type")
647)]
648pub struct Blockquote {
649    pub values: Vec<Node>,
650    #[cfg_attr(feature = "json", serde(skip))]
651    pub position: Option<Position>,
652}
653
654#[derive(Debug, Clone, PartialEq)]
655#[cfg_attr(
656    feature = "json",
657    derive(serde::Serialize, serde::Deserialize),
658    serde(rename_all = "camelCase", tag = "type")
659)]
660pub struct Delete {
661    pub values: Vec<Node>,
662    #[cfg_attr(feature = "json", serde(skip))]
663    pub position: Option<Position>,
664}
665
666#[derive(Debug, Clone, PartialEq)]
667#[cfg_attr(
668    feature = "json",
669    derive(serde::Serialize, serde::Deserialize),
670    serde(rename_all = "camelCase", tag = "type")
671)]
672pub struct Emphasis {
673    pub values: Vec<Node>,
674    #[cfg_attr(feature = "json", serde(skip))]
675    pub position: Option<Position>,
676}
677
678#[derive(Debug, Clone, PartialEq)]
679#[cfg_attr(
680    feature = "json",
681    derive(serde::Serialize, serde::Deserialize),
682    serde(rename_all = "camelCase", tag = "type")
683)]
684pub struct Strong {
685    pub values: Vec<Node>,
686    #[cfg_attr(feature = "json", serde(skip))]
687    pub position: Option<Position>,
688}
689
690#[derive(Debug, Clone, PartialEq)]
691#[cfg_attr(
692    feature = "json",
693    derive(serde::Serialize, serde::Deserialize),
694    serde(rename_all = "camelCase", tag = "type")
695)]
696pub struct Break {
697    #[cfg_attr(feature = "json", serde(skip))]
698    pub position: Option<Position>,
699}
700
701#[derive(Debug, Clone, PartialEq)]
702#[cfg_attr(
703    feature = "json",
704    derive(serde::Serialize, serde::Deserialize),
705    serde(rename_all = "camelCase", tag = "type")
706)]
707pub struct HorizontalRule {
708    #[cfg_attr(feature = "json", serde(skip))]
709    pub position: Option<Position>,
710}
711
712#[derive(Debug, Clone)]
713#[cfg_attr(
714    feature = "json",
715    derive(serde::Serialize, serde::Deserialize),
716    serde(rename_all = "camelCase", untagged)
717)]
718pub enum Node {
719    Blockquote(Blockquote),
720    Break(Break),
721    Definition(Definition),
722    Delete(Delete),
723    Heading(Heading),
724    Emphasis(Emphasis),
725    Footnote(Footnote),
726    FootnoteRef(FootnoteRef),
727    Html(Html),
728    Yaml(Yaml),
729    Toml(Toml),
730    Image(Image),
731    ImageRef(ImageRef),
732    CodeInline(CodeInline),
733    MathInline(MathInline),
734    Link(Link),
735    LinkRef(LinkRef),
736    Math(Math),
737    List(List),
738    TableHeader(TableHeader),
739    TableRow(TableRow),
740    TableCell(TableCell),
741    Code(Code),
742    Strong(Strong),
743    HorizontalRule(HorizontalRule),
744    MdxFlowExpression(MdxFlowExpression),
745    MdxJsxFlowElement(MdxJsxFlowElement),
746    MdxJsxTextElement(MdxJsxTextElement),
747    MdxTextExpression(MdxTextExpression),
748    MdxJsEsm(MdxJsEsm),
749    Text(Text),
750    Fragment(Fragment),
751    Empty,
752}
753
754impl PartialEq for Node {
755    fn eq(&self, other: &Self) -> bool {
756        self.to_string() == other.to_string()
757    }
758}
759
760impl PartialOrd for Node {
761    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
762        let (self_node, other_node) = (self, other);
763        let self_pos = self_node.position();
764        let other_pos = other_node.position();
765
766        match (self_pos, other_pos) {
767            (Some(self_pos), Some(other_pos)) => match self_pos.start.line.cmp(&other_pos.start.line) {
768                std::cmp::Ordering::Equal => self_pos.start.column.partial_cmp(&other_pos.start.column),
769                ordering => Some(ordering),
770            },
771            (Some(_), None) => Some(std::cmp::Ordering::Less),
772            (None, Some(_)) => Some(std::cmp::Ordering::Greater),
773            (None, None) => Some(self.name().cmp(&other.name())),
774        }
775    }
776}
777
778#[derive(Debug, Clone, PartialEq)]
779#[cfg_attr(
780    feature = "json",
781    derive(serde::Serialize, serde::Deserialize),
782    serde(rename_all = "camelCase")
783)]
784pub struct Position {
785    pub start: Point,
786    pub end: Point,
787}
788
789#[derive(Debug, Clone, PartialEq)]
790#[cfg_attr(
791    feature = "json",
792    derive(serde::Serialize, serde::Deserialize),
793    serde(rename_all = "camelCase")
794)]
795pub struct Point {
796    pub line: usize,
797    pub column: usize,
798}
799
800impl From<markdown::unist::Position> for Position {
801    fn from(value: markdown::unist::Position) -> Self {
802        Self {
803            start: Point {
804                line: value.start.line,
805                column: value.start.column,
806            },
807            end: Point {
808                line: value.end.line,
809                column: value.end.column,
810            },
811        }
812    }
813}
814
815impl From<String> for Node {
816    fn from(value: String) -> Self {
817        Self::Text(Text { value, position: None })
818    }
819}
820
821impl From<&str> for Node {
822    fn from(value: &str) -> Self {
823        Self::Text(Text {
824            value: value.to_string(),
825            position: None,
826        })
827    }
828}
829
830impl Display for Node {
831    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
832        write!(f, "{}", self.to_string_with(&RenderOptions::default()))
833    }
834}
835
836impl Node {
837    pub fn map_values<E, F>(&self, f: &mut F) -> Result<Node, E>
838    where
839        E: std::error::Error,
840        F: FnMut(&Node) -> Result<Node, E>,
841    {
842        Self::_map_values(self.clone(), f)
843    }
844
845    fn _map_values<E, F>(node: Node, f: &mut F) -> Result<Node, E>
846    where
847        E: std::error::Error,
848        F: FnMut(&Node) -> Result<Node, E>,
849    {
850        match f(&node)? {
851            Node::Fragment(mut v) => {
852                let values = v
853                    .values
854                    .into_iter()
855                    .map(|node| Self::_map_values(node, f))
856                    .collect::<Result<Vec<_>, _>>();
857                match values {
858                    Ok(values) => {
859                        v.values = values;
860                        Ok(Node::Fragment(v))
861                    }
862                    Err(e) => Err(e),
863                }
864            }
865            node => Ok(node),
866        }
867    }
868
869    pub fn to_fragment(&self) -> Node {
870        match self.clone() {
871            Node::List(List { values, .. })
872            | Node::TableCell(TableCell { values, .. })
873            | Node::TableRow(TableRow { values, .. })
874            | Node::Link(Link { values, .. })
875            | Node::Footnote(Footnote { values, .. })
876            | Node::LinkRef(LinkRef { values, .. })
877            | Node::Heading(Heading { values, .. })
878            | Node::Blockquote(Blockquote { values, .. })
879            | Node::Delete(Delete { values, .. })
880            | Node::Emphasis(Emphasis { values, .. })
881            | Node::Strong(Strong { values, .. }) => Self::Fragment(Fragment { values }),
882            node @ Node::Fragment(_) => node,
883            _ => Self::Empty,
884        }
885    }
886
887    pub fn apply_fragment(&mut self, fragment: Node) {
888        Self::_apply_fragment(self, fragment)
889    }
890
891    fn _apply_fragment(node: &mut Node, fragment: Node) {
892        match node {
893            Node::List(List { values, .. })
894            | Node::TableCell(TableCell { values, .. })
895            | Node::TableRow(TableRow { values, .. })
896            | Node::Link(Link { values, .. })
897            | Node::Footnote(Footnote { values, .. })
898            | Node::LinkRef(LinkRef { values, .. })
899            | Node::Heading(Heading { values, .. })
900            | Node::Blockquote(Blockquote { values, .. })
901            | Node::Delete(Delete { values, .. })
902            | Node::Emphasis(Emphasis { values, .. })
903            | Node::Strong(Strong { values, .. }) => {
904                if let Node::Fragment(Fragment { values: new_values }) = fragment {
905                    let new_values = values
906                        .iter()
907                        .zip(new_values)
908                        .map(|(current_value, new_value)| {
909                            if new_value.is_empty() {
910                                current_value.clone()
911                            } else if new_value.is_fragment() {
912                                let mut current_value = current_value.clone();
913                                Self::_apply_fragment(&mut current_value, new_value);
914                                current_value
915                            } else {
916                                new_value
917                            }
918                        })
919                        .collect::<Vec<_>>();
920                    *values = new_values;
921                }
922            }
923            _ => {}
924        }
925    }
926
927    pub fn to_string_with(&self, options: &RenderOptions) -> String {
928        match self.clone() {
929            Self::List(List {
930                level,
931                checked,
932                values,
933                ordered,
934                index,
935                ..
936            }) => {
937                format!(
938                    "{}{} {}{}",
939                    "  ".repeat(level as usize),
940                    if ordered {
941                        format!("{}.", index + 1)
942                    } else {
943                        options.list_style.to_string()
944                    },
945                    checked.map(|it| if it { "[x] " } else { "[ ] " }).unwrap_or_else(|| ""),
946                    values_to_string(&values, options)
947                )
948            }
949            Self::TableRow(TableRow { values, .. }) => values
950                .iter()
951                .map(|cell| cell.to_string_with(options))
952                .collect::<String>(),
953            Self::TableCell(TableCell {
954                last_cell_in_row,
955                last_cell_of_in_table,
956                values,
957                ..
958            }) => {
959                if last_cell_in_row || last_cell_of_in_table {
960                    format!("|{}|", values_to_string(&values, options))
961                } else {
962                    format!("|{}", values_to_string(&values, options))
963                }
964            }
965            Self::TableHeader(TableHeader { align, .. }) => {
966                format!("|{}|", align.iter().map(|a| a.to_string()).join("|"))
967            }
968            Self::Blockquote(Blockquote { values, .. }) => values_to_string(&values, options)
969                .split('\n')
970                .map(|line| format!("> {}", line))
971                .join("\n"),
972            Self::Code(Code {
973                value,
974                lang,
975                fence,
976                meta,
977                ..
978            }) => {
979                let meta = meta.as_deref().map(|meta| format!(" {}", meta)).unwrap_or_default();
980
981                match lang {
982                    Some(lang) => format!("```{}{}\n{}\n```", lang, meta, value),
983                    None if fence => {
984                        format!("```{}\n{}\n```", lang.as_deref().unwrap_or(""), value)
985                    }
986                    None => value.lines().map(|line| format!("    {}", line)).join("\n"),
987                }
988            }
989            Self::Definition(Definition {
990                ident,
991                label,
992                url,
993                title,
994                ..
995            }) => {
996                format!(
997                    "[{}]: {}{}",
998                    label.unwrap_or(ident),
999                    url.to_string_with(options),
1000                    title
1001                        .map(|title| format!(" {}", title.to_string_with(options)))
1002                        .unwrap_or_default()
1003                )
1004            }
1005            Self::Delete(Delete { values, .. }) => {
1006                format!("~~{}~~", values_to_string(&values, options))
1007            }
1008            Self::Emphasis(Emphasis { values, .. }) => {
1009                format!("*{}*", values_to_string(&values, options))
1010            }
1011            Self::Footnote(Footnote { values, ident, .. }) => {
1012                format!("[^{}]: {}", ident, values_to_string(&values, options))
1013            }
1014            Self::FootnoteRef(FootnoteRef { label, .. }) => {
1015                format!("[^{}]", label.unwrap_or_default())
1016            }
1017            Self::Heading(Heading { depth, values, .. }) => {
1018                format!("{} {}", "#".repeat(depth as usize), values_to_string(&values, options))
1019            }
1020            Self::Html(Html { value, .. }) => value,
1021            Self::Image(Image { alt, url, title, .. }) => format!(
1022                "![{}]({}{})",
1023                alt,
1024                url.replace(' ', "%20"),
1025                title.map(|it| format!(" \"{}\"", it)).unwrap_or_default()
1026            ),
1027            Self::ImageRef(ImageRef { alt, ident, label, .. }) => {
1028                if alt == ident {
1029                    format!("![{}]", ident)
1030                } else {
1031                    format!("![{}][{}]", alt, label.unwrap_or(ident))
1032                }
1033            }
1034            Self::CodeInline(CodeInline { value, .. }) => {
1035                format!("`{}`", value)
1036            }
1037            Self::MathInline(MathInline { value, .. }) => {
1038                format!("${}$", value)
1039            }
1040            Self::Link(Link { url, title, values, .. }) => {
1041                format!(
1042                    "[{}]({}{})",
1043                    values_to_string(&values, options),
1044                    url.to_string_with(options),
1045                    title
1046                        .map(|title| format!(" {}", title.to_string_with(options)))
1047                        .unwrap_or_default(),
1048                )
1049            }
1050            Self::LinkRef(LinkRef { values, label, .. }) => {
1051                let ident = values_to_string(&values, options);
1052
1053                label
1054                    .map(|label| {
1055                        if label == ident {
1056                            format!("[{}]", ident)
1057                        } else {
1058                            format!("[{}][{}]", ident, label)
1059                        }
1060                    })
1061                    .unwrap_or(format!("[{}]", ident))
1062            }
1063            Self::Math(Math { value, .. }) => format!("$$\n{}\n$$", value),
1064            Self::Text(Text { value, .. }) => value,
1065            Self::MdxFlowExpression(mdx_flow_expression) => {
1066                format!("{{{}}}", mdx_flow_expression.value)
1067            }
1068            Self::MdxJsxFlowElement(mdx_jsx_flow_element) => {
1069                let name = mdx_jsx_flow_element.name.unwrap_or_default();
1070                let attributes = if mdx_jsx_flow_element.attributes.is_empty() {
1071                    "".to_string()
1072                } else {
1073                    format!(
1074                        " {}",
1075                        mdx_jsx_flow_element
1076                            .attributes
1077                            .into_iter()
1078                            .map(Self::mdx_attribute_content_to_string)
1079                            .join(" ")
1080                    )
1081                };
1082
1083                if mdx_jsx_flow_element.children.is_empty() {
1084                    format!("<{}{} />", name, attributes,)
1085                } else {
1086                    format!(
1087                        "<{}{}>{}</{}>",
1088                        name,
1089                        attributes,
1090                        values_to_string(&mdx_jsx_flow_element.children, options),
1091                        name
1092                    )
1093                }
1094            }
1095            Self::MdxJsxTextElement(mdx_jsx_text_element) => {
1096                let name = mdx_jsx_text_element.name.unwrap_or_default();
1097                let attributes = if mdx_jsx_text_element.attributes.is_empty() {
1098                    "".to_string()
1099                } else {
1100                    format!(
1101                        " {}",
1102                        mdx_jsx_text_element
1103                            .attributes
1104                            .into_iter()
1105                            .map(Self::mdx_attribute_content_to_string)
1106                            .join(" ")
1107                    )
1108                };
1109
1110                if mdx_jsx_text_element.children.is_empty() {
1111                    format!("<{}{} />", name, attributes,)
1112                } else {
1113                    format!(
1114                        "<{}{}>{}</{}>",
1115                        name,
1116                        attributes,
1117                        values_to_string(&mdx_jsx_text_element.children, options),
1118                        name
1119                    )
1120                }
1121            }
1122            Self::MdxTextExpression(mdx_text_expression) => {
1123                format!("{{{}}}", mdx_text_expression.value)
1124            }
1125            Self::MdxJsEsm(mdxjs_esm) => mdxjs_esm.value.to_string(),
1126            Self::Strong(Strong { values, .. }) => {
1127                format!(
1128                    "**{}**",
1129                    values
1130                        .iter()
1131                        .map(|value| value.to_string_with(options))
1132                        .collect::<String>()
1133                )
1134            }
1135            Self::Yaml(Yaml { value, .. }) => format!("---\n{}\n---", value),
1136            Self::Toml(Toml { value, .. }) => format!("+++\n{}\n+++", value),
1137            Self::Break(_) => "\\".to_string(),
1138            Self::HorizontalRule(_) => "---".to_string(),
1139            Self::Fragment(Fragment { values }) => values
1140                .iter()
1141                .map(|value| value.to_string_with(options))
1142                .collect::<String>(),
1143            Self::Empty => String::new(),
1144        }
1145    }
1146
1147    pub fn node_values(&self) -> Vec<Node> {
1148        match self.clone() {
1149            Self::Blockquote(v) => v.values,
1150            Self::Delete(v) => v.values,
1151            Self::Heading(h) => h.values,
1152            Self::Emphasis(v) => v.values,
1153            Self::List(l) => l.values,
1154            Self::Strong(v) => v.values,
1155            _ => vec![self.clone()],
1156        }
1157    }
1158
1159    pub fn find_at_index(&self, index: usize) -> Option<Node> {
1160        match self {
1161            Self::Blockquote(v) => v.values.get(index).cloned(),
1162            Self::Delete(v) => v.values.get(index).cloned(),
1163            Self::Emphasis(v) => v.values.get(index).cloned(),
1164            Self::Strong(v) => v.values.get(index).cloned(),
1165            Self::Heading(v) => v.values.get(index).cloned(),
1166            Self::List(v) => v.values.get(index).cloned(),
1167            Self::TableCell(v) => v.values.get(index).cloned(),
1168            Self::TableRow(v) => v.values.get(index).cloned(),
1169            _ => None,
1170        }
1171    }
1172
1173    pub fn value(&self) -> String {
1174        match self.clone() {
1175            Self::Blockquote(v) => values_to_value(v.values),
1176            Self::Definition(d) => d.url.as_str().to_string(),
1177            Self::Delete(v) => values_to_value(v.values),
1178            Self::Heading(h) => values_to_value(h.values),
1179            Self::Emphasis(v) => values_to_value(v.values),
1180            Self::Footnote(f) => values_to_value(f.values),
1181            Self::FootnoteRef(f) => f.ident,
1182            Self::Html(v) => v.value,
1183            Self::Yaml(v) => v.value,
1184            Self::Toml(v) => v.value,
1185            Self::Image(i) => i.url,
1186            Self::ImageRef(i) => i.ident,
1187            Self::CodeInline(v) => v.value.to_string(),
1188            Self::MathInline(v) => v.value.to_string(),
1189            Self::Link(l) => l.url.as_str().to_string(),
1190            Self::LinkRef(l) => l.ident,
1191            Self::Math(v) => v.value,
1192            Self::List(l) => values_to_value(l.values),
1193            Self::TableCell(c) => values_to_value(c.values),
1194            Self::TableRow(c) => values_to_value(c.values),
1195            Self::Code(c) => c.value,
1196            Self::Strong(v) => values_to_value(v.values),
1197            Self::Text(t) => t.value,
1198            Self::Break { .. } => String::new(),
1199            Self::TableHeader(_) => String::new(),
1200            Self::MdxFlowExpression(mdx) => mdx.value.to_string(),
1201            Self::MdxJsxFlowElement(mdx) => values_to_value(mdx.children),
1202            Self::MdxTextExpression(mdx) => mdx.value.to_string(),
1203            Self::MdxJsxTextElement(mdx) => values_to_value(mdx.children),
1204            Self::MdxJsEsm(mdx) => mdx.value.to_string(),
1205            Self::HorizontalRule { .. } => String::new(),
1206            Self::Fragment(v) => values_to_value(v.values),
1207            Self::Empty => String::new(),
1208        }
1209    }
1210
1211    pub fn name(&self) -> SmolStr {
1212        match self {
1213            Self::Blockquote(_) => "blockquote".into(),
1214            Self::Break { .. } => "break".into(),
1215            Self::Definition(_) => "definition".into(),
1216            Self::Delete(_) => "delete".into(),
1217            Self::Heading(Heading { depth, .. }) => match depth {
1218                1 => "h1".into(),
1219                2 => "h2".into(),
1220                3 => "h3".into(),
1221                4 => "h4".into(),
1222                5 => "h5".into(),
1223                6 => "h6".into(),
1224                _ => "h".into(),
1225            },
1226            Self::Emphasis(_) => "emphasis".into(),
1227            Self::Footnote(_) => "footnote".into(),
1228            Self::FootnoteRef(_) => "footnoteref".into(),
1229            Self::Html(_) => "html".into(),
1230            Self::Yaml(_) => "yaml".into(),
1231            Self::Toml(_) => "toml".into(),
1232            Self::Image(_) => "image".into(),
1233            Self::ImageRef(_) => "image_ref".into(),
1234            Self::CodeInline(_) => "code_inline".into(),
1235            Self::MathInline(_) => "math_inline".into(),
1236            Self::Link(_) => "link".into(),
1237            Self::LinkRef(_) => "link_ref".into(),
1238            Self::Math(_) => "math".into(),
1239            Self::List(_) => "list".into(),
1240            Self::TableHeader(_) => "table_header".into(),
1241            Self::TableRow(_) => "table_row".into(),
1242            Self::TableCell(_) => "table_cell".into(),
1243            Self::Code(_) => "code".into(),
1244            Self::Strong(_) => "strong".into(),
1245            Self::HorizontalRule { .. } => "Horizontal_rule".into(),
1246            Self::MdxFlowExpression(_) => "mdx_flow_expression".into(),
1247            Self::MdxJsxFlowElement(_) => "mdx_jsx_flow_element".into(),
1248            Self::MdxJsxTextElement(_) => "mdx_jsx_text_element".into(),
1249            Self::MdxTextExpression(_) => "mdx_text_expression".into(),
1250            Self::MdxJsEsm(_) => "mdx_js_esm".into(),
1251            Self::Text(_) => "text".into(),
1252            Self::Fragment(_) | Self::Empty => "".into(),
1253        }
1254    }
1255
1256    pub fn set_position(&mut self, pos: Option<Position>) {
1257        match self {
1258            Self::Blockquote(v) => v.position = pos,
1259            Self::Definition(d) => d.position = pos,
1260            Self::Delete(v) => v.position = pos,
1261            Self::Heading(h) => h.position = pos,
1262            Self::Emphasis(v) => v.position = pos,
1263            Self::Footnote(f) => f.position = pos,
1264            Self::FootnoteRef(f) => f.position = pos,
1265            Self::Html(v) => v.position = pos,
1266            Self::Yaml(v) => v.position = pos,
1267            Self::Toml(v) => v.position = pos,
1268            Self::Image(i) => i.position = pos,
1269            Self::ImageRef(i) => i.position = pos,
1270            Self::CodeInline(v) => v.position = pos,
1271            Self::MathInline(v) => v.position = pos,
1272            Self::Link(l) => l.position = pos,
1273            Self::LinkRef(l) => l.position = pos,
1274            Self::Math(v) => v.position = pos,
1275            Self::Code(c) => c.position = pos,
1276            Self::TableCell(c) => c.position = pos,
1277            Self::TableRow(r) => r.position = pos,
1278            Self::TableHeader(c) => c.position = pos,
1279            Self::List(l) => l.position = pos,
1280            Self::Strong(s) => s.position = pos,
1281            Self::MdxFlowExpression(m) => m.position = pos,
1282            Self::MdxTextExpression(m) => m.position = pos,
1283            Self::MdxJsEsm(m) => m.position = pos,
1284            Self::MdxJsxFlowElement(m) => m.position = pos,
1285            Self::MdxJsxTextElement(m) => m.position = pos,
1286            Self::Break(b) => b.position = pos,
1287            Self::HorizontalRule(h) => h.position = pos,
1288            Self::Text(t) => t.position = pos,
1289            Self::Fragment(_) | Self::Empty => {}
1290        }
1291    }
1292
1293    pub fn position(&self) -> Option<Position> {
1294        match self {
1295            Self::Blockquote(v) => v.position.clone(),
1296            Self::Definition(d) => d.position.clone(),
1297            Self::Delete(v) => v.position.clone(),
1298            Self::Heading(h) => h.position.clone(),
1299            Self::Emphasis(v) => v.position.clone(),
1300            Self::Footnote(f) => f.position.clone(),
1301            Self::FootnoteRef(f) => f.position.clone(),
1302            Self::Html(v) => v.position.clone(),
1303            Self::Yaml(v) => v.position.clone(),
1304            Self::Toml(v) => v.position.clone(),
1305            Self::Image(i) => i.position.clone(),
1306            Self::ImageRef(i) => i.position.clone(),
1307            Self::CodeInline(v) => v.position.clone(),
1308            Self::MathInline(v) => v.position.clone(),
1309            Self::Link(l) => l.position.clone(),
1310            Self::LinkRef(l) => l.position.clone(),
1311            Self::Math(v) => v.position.clone(),
1312            Self::Code(c) => c.position.clone(),
1313            Self::TableCell(c) => c.position.clone(),
1314            Self::TableRow(r) => r.position.clone(),
1315            Self::TableHeader(c) => c.position.clone(),
1316            Self::List(l) => l.position.clone(),
1317            Self::Strong(s) => s.position.clone(),
1318            Self::MdxFlowExpression(m) => m.position.clone(),
1319            Self::MdxTextExpression(m) => m.position.clone(),
1320            Self::MdxJsEsm(m) => m.position.clone(),
1321            Self::MdxJsxFlowElement(m) => m.position.clone(),
1322            Self::MdxJsxTextElement(m) => m.position.clone(),
1323            Self::Break(b) => b.position.clone(),
1324            Self::Text(t) => t.position.clone(),
1325            Self::HorizontalRule(h) => h.position.clone(),
1326            Self::Fragment(v) => {
1327                let positions: Vec<Position> = v.values.iter().filter_map(|node| node.position()).collect();
1328
1329                match (positions.first(), positions.last()) {
1330                    (Some(start), Some(end)) => Some(Position {
1331                        start: start.start.clone(),
1332                        end: end.end.clone(),
1333                    }),
1334                    _ => None,
1335                }
1336            }
1337            Self::Empty => None,
1338        }
1339    }
1340
1341    pub fn is_empty(&self) -> bool {
1342        matches!(self, Self::Empty)
1343    }
1344
1345    pub fn is_fragment(&self) -> bool {
1346        matches!(self, Self::Fragment(_))
1347    }
1348
1349    pub fn is_empty_fragment(&self) -> bool {
1350        if let Self::Fragment(_) = self {
1351            Self::_fragment_inner_nodes(self).is_empty()
1352        } else {
1353            false
1354        }
1355    }
1356
1357    fn _fragment_inner_nodes(node: &Node) -> Vec<Node> {
1358        if let Self::Fragment(fragment) = node {
1359            fragment.values.iter().flat_map(Self::_fragment_inner_nodes).collect()
1360        } else {
1361            vec![node.clone()]
1362        }
1363    }
1364
1365    pub fn is_inline_code(&self) -> bool {
1366        matches!(self, Self::CodeInline(_))
1367    }
1368
1369    pub fn is_inline_math(&self) -> bool {
1370        matches!(self, Self::MathInline(_))
1371    }
1372
1373    pub fn is_strong(&self) -> bool {
1374        matches!(self, Self::Strong(_))
1375    }
1376
1377    pub fn is_list(&self) -> bool {
1378        matches!(self, Self::List(_))
1379    }
1380
1381    pub fn is_table_cell(&self) -> bool {
1382        matches!(self, Self::TableCell(_))
1383    }
1384
1385    pub fn is_table_row(&self) -> bool {
1386        matches!(self, Self::TableRow(_))
1387    }
1388
1389    pub fn is_emphasis(&self) -> bool {
1390        matches!(self, Self::Emphasis(_))
1391    }
1392
1393    pub fn is_delete(&self) -> bool {
1394        matches!(self, Self::Delete(_))
1395    }
1396
1397    pub fn is_link(&self) -> bool {
1398        matches!(self, Self::Link(_))
1399    }
1400
1401    pub fn is_link_ref(&self) -> bool {
1402        matches!(self, Self::LinkRef(_))
1403    }
1404
1405    pub fn is_text(&self) -> bool {
1406        matches!(self, Self::Text(_))
1407    }
1408
1409    pub fn is_image(&self) -> bool {
1410        matches!(self, Self::Image(_))
1411    }
1412
1413    pub fn is_horizontal_rule(&self) -> bool {
1414        matches!(self, Self::HorizontalRule { .. })
1415    }
1416
1417    pub fn is_blockquote(&self) -> bool {
1418        matches!(self, Self::Blockquote(_))
1419    }
1420
1421    pub fn is_html(&self) -> bool {
1422        matches!(self, Self::Html { .. })
1423    }
1424
1425    pub fn is_footnote(&self) -> bool {
1426        matches!(self, Self::Footnote(_))
1427    }
1428
1429    pub fn is_mdx_jsx_flow_element(&self) -> bool {
1430        matches!(self, Self::MdxJsxFlowElement(MdxJsxFlowElement { .. }))
1431    }
1432
1433    pub fn is_mdx_js_esm(&self) -> bool {
1434        matches!(self, Self::MdxJsEsm(MdxJsEsm { .. }))
1435    }
1436
1437    pub fn is_toml(&self) -> bool {
1438        matches!(self, Self::Toml { .. })
1439    }
1440
1441    pub fn is_yaml(&self) -> bool {
1442        matches!(self, Self::Yaml { .. })
1443    }
1444
1445    pub fn is_break(&self) -> bool {
1446        matches!(self, Self::Break { .. })
1447    }
1448
1449    pub fn is_mdx_text_expression(&self) -> bool {
1450        matches!(self, Self::MdxTextExpression(MdxTextExpression { .. }))
1451    }
1452
1453    pub fn is_footnote_ref(&self) -> bool {
1454        matches!(self, Self::FootnoteRef { .. })
1455    }
1456
1457    pub fn is_image_ref(&self) -> bool {
1458        matches!(self, Self::ImageRef(_))
1459    }
1460
1461    pub fn is_mdx_jsx_text_element(&self) -> bool {
1462        matches!(self, Self::MdxJsxTextElement(MdxJsxTextElement { .. }))
1463    }
1464
1465    pub fn is_math(&self) -> bool {
1466        matches!(self, Self::Math(_))
1467    }
1468
1469    pub fn is_mdx_flow_expression(&self) -> bool {
1470        matches!(self, Self::MdxFlowExpression(MdxFlowExpression { .. }))
1471    }
1472
1473    pub fn is_definition(&self) -> bool {
1474        matches!(self, Self::Definition(_))
1475    }
1476
1477    pub fn is_code(&self, lang: Option<SmolStr>) -> bool {
1478        if let Self::Code(Code { lang: node_lang, .. }) = &self {
1479            if lang.is_none() {
1480                true
1481            } else {
1482                node_lang.clone().unwrap_or_default() == lang.unwrap_or_default()
1483            }
1484        } else {
1485            false
1486        }
1487    }
1488
1489    pub fn is_heading(&self, depth: Option<u8>) -> bool {
1490        if let Self::Heading(Heading {
1491            depth: heading_depth, ..
1492        }) = &self
1493        {
1494            depth.is_none() || *heading_depth == depth.unwrap()
1495        } else {
1496            false
1497        }
1498    }
1499
1500    pub fn with_value(&self, value: &str) -> Self {
1501        match self.clone() {
1502            Self::Blockquote(mut v) => {
1503                if let Some(node) = v.values.first() {
1504                    v.values[0] = node.with_value(value);
1505                }
1506
1507                Self::Blockquote(v)
1508            }
1509            Self::Delete(mut v) => {
1510                if let Some(node) = v.values.first() {
1511                    v.values[0] = node.with_value(value);
1512                }
1513
1514                Self::Delete(v)
1515            }
1516            Self::Emphasis(mut v) => {
1517                if let Some(node) = v.values.first() {
1518                    v.values[0] = node.with_value(value);
1519                }
1520
1521                Self::Emphasis(v)
1522            }
1523            Self::Html(mut html) => {
1524                html.value = value.to_string();
1525                Self::Html(html)
1526            }
1527            Self::Yaml(mut yaml) => {
1528                yaml.value = value.to_string();
1529                Self::Yaml(yaml)
1530            }
1531            Self::Toml(mut toml) => {
1532                toml.value = value.to_string();
1533                Self::Toml(toml)
1534            }
1535            Self::CodeInline(mut code) => {
1536                code.value = value.into();
1537                Self::CodeInline(code)
1538            }
1539            Self::MathInline(mut math) => {
1540                math.value = value.into();
1541                Self::MathInline(math)
1542            }
1543            Self::Math(mut math) => {
1544                math.value = value.to_string();
1545                Self::Math(math)
1546            }
1547            Self::List(mut v) => {
1548                if let Some(node) = v.values.first() {
1549                    v.values[0] = node.with_value(value);
1550                }
1551
1552                Self::List(v)
1553            }
1554            Self::TableCell(mut v) => {
1555                if let Some(node) = v.values.first() {
1556                    v.values[0] = node.with_value(value);
1557                }
1558
1559                Self::TableCell(v)
1560            }
1561            Self::TableRow(mut row) => {
1562                row.values = row
1563                    .values
1564                    .iter()
1565                    .zip(value.split(","))
1566                    .map(|(cell, value)| cell.with_value(value))
1567                    .collect::<Vec<_>>();
1568
1569                Self::TableRow(row)
1570            }
1571            Self::Strong(mut v) => {
1572                if let Some(node) = v.values.first() {
1573                    v.values[0] = node.with_value(value);
1574                }
1575
1576                Self::Strong(v)
1577            }
1578            Self::Code(mut code) => {
1579                code.value = value.to_string();
1580                Self::Code(code)
1581            }
1582            Self::Image(mut image) => {
1583                image.url = value.to_string();
1584                Self::Image(image)
1585            }
1586            Self::ImageRef(mut image) => {
1587                image.ident = value.to_string();
1588                image.label = Some(value.to_string());
1589                Self::ImageRef(image)
1590            }
1591            Self::Link(mut link) => {
1592                link.url = Url(value.to_string());
1593                Self::Link(link)
1594            }
1595            Self::LinkRef(mut v) => {
1596                v.label = Some(value.to_string());
1597                v.ident = value.to_string();
1598                Self::LinkRef(v)
1599            }
1600            Self::Footnote(mut footnote) => {
1601                footnote.ident = value.to_string();
1602                Self::Footnote(footnote)
1603            }
1604            Self::FootnoteRef(mut footnote) => {
1605                footnote.ident = value.to_string();
1606                footnote.label = Some(value.to_string());
1607                Self::FootnoteRef(footnote)
1608            }
1609            Self::Heading(mut v) => {
1610                if let Some(node) = v.values.first() {
1611                    v.values[0] = node.with_value(value);
1612                }
1613
1614                Self::Heading(v)
1615            }
1616            Self::Definition(mut def) => {
1617                def.url = Url(value.to_string());
1618                Self::Definition(def)
1619            }
1620            node @ Self::Break { .. } => node,
1621            node @ Self::TableHeader(_) => node,
1622            node @ Self::HorizontalRule { .. } => node,
1623            Self::Text(mut text) => {
1624                text.value = value.to_string();
1625                Self::Text(text)
1626            }
1627            Self::MdxFlowExpression(mut mdx) => {
1628                mdx.value = value.into();
1629                Self::MdxFlowExpression(mdx)
1630            }
1631            Self::MdxTextExpression(mut mdx) => {
1632                mdx.value = value.into();
1633                Self::MdxTextExpression(mdx)
1634            }
1635            Self::MdxJsEsm(mut mdx) => {
1636                mdx.value = value.into();
1637                Self::MdxJsEsm(mdx)
1638            }
1639            Self::MdxJsxFlowElement(mut mdx) => {
1640                if let Some(node) = mdx.children.first() {
1641                    mdx.children[0] = node.with_value(value);
1642                }
1643
1644                Self::MdxJsxFlowElement(MdxJsxFlowElement {
1645                    name: mdx.name,
1646                    attributes: mdx.attributes,
1647                    children: mdx.children,
1648                    ..mdx
1649                })
1650            }
1651            Self::MdxJsxTextElement(mut mdx) => {
1652                if let Some(node) = mdx.children.first() {
1653                    mdx.children[0] = node.with_value(value);
1654                }
1655
1656                Self::MdxJsxTextElement(MdxJsxTextElement {
1657                    name: mdx.name,
1658                    attributes: mdx.attributes,
1659                    children: mdx.children,
1660                    ..mdx
1661                })
1662            }
1663            node @ Self::Fragment(_) | node @ Self::Empty => node,
1664        }
1665    }
1666
1667    pub fn with_children_value(&self, value: &str, index: usize) -> Self {
1668        match self.clone() {
1669            Self::Blockquote(mut v) => {
1670                if v.values.get(index).is_some() {
1671                    v.values[index] = v.values[index].with_value(value);
1672                }
1673
1674                Self::Blockquote(v)
1675            }
1676            Self::Delete(mut v) => {
1677                if v.values.get(index).is_some() {
1678                    v.values[index] = v.values[index].with_value(value);
1679                }
1680
1681                Self::Delete(v)
1682            }
1683            Self::Emphasis(mut v) => {
1684                if v.values.get(index).is_some() {
1685                    v.values[index] = v.values[index].with_value(value);
1686                }
1687
1688                Self::Emphasis(v)
1689            }
1690            Self::List(mut v) => {
1691                if v.values.get(index).is_some() {
1692                    v.values[index] = v.values[index].with_value(value);
1693                }
1694
1695                Self::List(v)
1696            }
1697            Self::TableCell(mut v) => {
1698                if v.values.get(index).is_some() {
1699                    v.values[index] = v.values[index].with_value(value);
1700                }
1701
1702                Self::TableCell(v)
1703            }
1704            Self::Strong(mut v) => {
1705                if v.values.get(index).is_some() {
1706                    v.values[index] = v.values[index].with_value(value);
1707                }
1708
1709                Self::Strong(v)
1710            }
1711            Self::LinkRef(mut v) => {
1712                if v.values.get(index).is_some() {
1713                    v.values[index] = v.values[index].with_value(value);
1714                }
1715
1716                Self::LinkRef(v)
1717            }
1718            Self::Heading(mut v) => {
1719                if v.values.get(index).is_some() {
1720                    v.values[index] = v.values[index].with_value(value);
1721                }
1722
1723                Self::Heading(v)
1724            }
1725            Self::MdxJsxFlowElement(mut mdx) => {
1726                if let Some(node) = mdx.children.first() {
1727                    mdx.children[index] = node.with_value(value);
1728                }
1729
1730                Self::MdxJsxFlowElement(MdxJsxFlowElement {
1731                    name: mdx.name,
1732                    attributes: mdx.attributes,
1733                    children: mdx.children,
1734                    ..mdx
1735                })
1736            }
1737            Self::MdxJsxTextElement(mut mdx) => {
1738                if let Some(node) = mdx.children.first() {
1739                    mdx.children[index] = node.with_value(value);
1740                }
1741
1742                Self::MdxJsxTextElement(MdxJsxTextElement {
1743                    name: mdx.name,
1744                    attributes: mdx.attributes,
1745                    children: mdx.children,
1746                    ..mdx
1747                })
1748            }
1749            a => a,
1750        }
1751    }
1752
1753    /// Returns the value of the specified attribute, if present.
1754    pub fn attr(&self, attr: &str) -> Option<AttrValue> {
1755        match self {
1756            Node::Footnote(Footnote { ident, values, .. }) => match attr {
1757                "ident" => Some(AttrValue::String(ident.clone())),
1758                "value" => Some(AttrValue::String(values_to_string(values, &RenderOptions::default()))),
1759                "values" | "children" | "cn" => Some(AttrValue::Array(values.clone())),
1760                _ => None,
1761            },
1762            Node::Html(Html { value, .. }) => match attr {
1763                "value" => Some(AttrValue::String(value.clone())),
1764                _ => None,
1765            },
1766            Node::Text(Text { value, .. }) => match attr {
1767                "value" => Some(AttrValue::String(value.clone())),
1768                _ => None,
1769            },
1770            Node::Code(Code {
1771                value,
1772                lang,
1773                meta,
1774                fence,
1775                ..
1776            }) => match attr {
1777                "value" => Some(AttrValue::String(value.clone())),
1778                "lang" => lang.clone().map(AttrValue::String),
1779                "meta" => meta.clone().map(AttrValue::String),
1780                "fence" => Some(AttrValue::Boolean(*fence)),
1781                _ => None,
1782            },
1783            Node::CodeInline(CodeInline { value, .. }) => match attr {
1784                "value" => Some(AttrValue::String(value.to_string())),
1785                _ => None,
1786            },
1787            Node::MathInline(MathInline { value, .. }) => match attr {
1788                "value" => Some(AttrValue::String(value.to_string())),
1789                _ => None,
1790            },
1791            Node::Math(Math { value, .. }) => match attr {
1792                "value" => Some(AttrValue::String(value.clone())),
1793                _ => None,
1794            },
1795            Node::Yaml(Yaml { value, .. }) => match attr {
1796                "value" => Some(AttrValue::String(value.clone())),
1797                _ => None,
1798            },
1799            Node::Toml(Toml { value, .. }) => match attr {
1800                "value" => Some(AttrValue::String(value.clone())),
1801                _ => None,
1802            },
1803            Node::Image(Image { alt, url, title, .. }) => match attr {
1804                "alt" => Some(AttrValue::String(alt.clone())),
1805                "url" => Some(AttrValue::String(url.clone())),
1806                "title" => title.clone().map(AttrValue::String),
1807                _ => None,
1808            },
1809            Node::ImageRef(ImageRef { alt, ident, label, .. }) => match attr {
1810                "alt" => Some(AttrValue::String(alt.clone())),
1811                "ident" => Some(AttrValue::String(ident.clone())),
1812                "label" => label.clone().map(AttrValue::String),
1813                _ => None,
1814            },
1815            Node::Link(Link { url, title, values, .. }) => match attr {
1816                "url" => Some(AttrValue::String(url.as_str().to_string())),
1817                "title" => title.as_ref().map(|t| AttrValue::String(t.to_value())),
1818                "value" => Some(AttrValue::String(values_to_string(values, &RenderOptions::default()))),
1819                "values" | "children" | "cn" => Some(AttrValue::Array(values.clone())),
1820                _ => None,
1821            },
1822            Node::LinkRef(LinkRef { ident, label, .. }) => match attr {
1823                "ident" => Some(AttrValue::String(ident.clone())),
1824                "label" => label.clone().map(AttrValue::String),
1825                _ => None,
1826            },
1827            Node::FootnoteRef(FootnoteRef { ident, label, .. }) => match attr {
1828                "ident" => Some(AttrValue::String(ident.clone())),
1829                "label" => label.clone().map(AttrValue::String),
1830                _ => None,
1831            },
1832            Node::Definition(Definition {
1833                ident,
1834                url,
1835                title,
1836                label,
1837                ..
1838            }) => match attr {
1839                "ident" => Some(AttrValue::String(ident.clone())),
1840                "url" => Some(AttrValue::String(url.as_str().to_string())),
1841                "title" => title.as_ref().map(|t| AttrValue::String(t.to_value())),
1842                "label" => label.clone().map(AttrValue::String),
1843                _ => None,
1844            },
1845            Node::Heading(Heading { depth, values, .. }) => match attr {
1846                "depth" | "level" => Some(AttrValue::Integer(*depth as i64)),
1847                "value" => Some(AttrValue::String(values_to_string(values, &RenderOptions::default()))),
1848                "values" | "children" | "cn" => Some(AttrValue::Array(values.clone())),
1849                _ => None,
1850            },
1851            Node::List(List {
1852                index,
1853                level,
1854                ordered,
1855                checked,
1856                values,
1857                ..
1858            }) => match attr {
1859                "index" => Some(AttrValue::Integer(*index as i64)),
1860                "level" => Some(AttrValue::Integer(*level as i64)),
1861                "ordered" => Some(AttrValue::Boolean(*ordered)),
1862                "checked" => checked.map(AttrValue::Boolean),
1863                "value" => Some(AttrValue::String(values_to_string(values, &RenderOptions::default()))),
1864                "values" | "children" | "cn" => Some(AttrValue::Array(values.clone())),
1865                _ => None,
1866            },
1867            Node::TableCell(TableCell {
1868                column,
1869                row,
1870                last_cell_in_row,
1871                last_cell_of_in_table,
1872                values,
1873                ..
1874            }) => match attr {
1875                "column" => Some(AttrValue::Integer(*column as i64)),
1876                "row" => Some(AttrValue::Integer(*row as i64)),
1877                "last_cell_in_row" => Some(AttrValue::Boolean(*last_cell_in_row)),
1878                "last_cell_of_in_table" => Some(AttrValue::Boolean(*last_cell_of_in_table)),
1879                "value" => Some(AttrValue::String(values_to_string(values, &RenderOptions::default()))),
1880                "values" | "children" | "cn" => Some(AttrValue::Array(values.clone())),
1881                _ => None,
1882            },
1883            Node::TableHeader(TableHeader { align, .. }) => match attr {
1884                "align" => Some(AttrValue::String(
1885                    align.iter().map(|a| a.to_string()).collect::<Vec<_>>().join(","),
1886                )),
1887                _ => None,
1888            },
1889            Node::MdxFlowExpression(MdxFlowExpression { value, .. }) => match attr {
1890                "value" => Some(AttrValue::String(value.to_string())),
1891                _ => None,
1892            },
1893            Node::MdxTextExpression(MdxTextExpression { value, .. }) => match attr {
1894                "value" => Some(AttrValue::String(value.to_string())),
1895                _ => None,
1896            },
1897            Node::MdxJsEsm(MdxJsEsm { value, .. }) => match attr {
1898                "value" => Some(AttrValue::String(value.to_string())),
1899                _ => None,
1900            },
1901            Node::MdxJsxFlowElement(MdxJsxFlowElement { name, .. }) => match attr {
1902                "name" => name.clone().map(AttrValue::String),
1903                _ => None,
1904            },
1905            Node::MdxJsxTextElement(MdxJsxTextElement { name, .. }) => match attr {
1906                "name" => name.as_ref().map(|n| AttrValue::String(n.to_string())),
1907                _ => None,
1908            },
1909            Node::Strong(Strong { values, .. }) => match attr {
1910                "value" => Some(AttrValue::String(values_to_string(
1911                    values.as_ref(),
1912                    &RenderOptions::default(),
1913                ))),
1914                "values" | "children" | "cn" => Some(AttrValue::Array(values.clone())),
1915                _ => None,
1916            },
1917            Node::Blockquote(Blockquote { values, .. }) => match attr {
1918                "value" => Some(AttrValue::String(values_to_string(values, &RenderOptions::default()))),
1919                "values" | "children" | "cn" => Some(AttrValue::Array(values.clone())),
1920                _ => None,
1921            },
1922            Node::Delete(Delete { values, .. }) => match attr {
1923                "value" => Some(AttrValue::String(values_to_string(values, &RenderOptions::default()))),
1924                "values" | "children" | "cn" => Some(AttrValue::Array(values.clone())),
1925                _ => None,
1926            },
1927            Node::Emphasis(Emphasis { values, .. }) => match attr {
1928                "value" => Some(AttrValue::String(values_to_string(values, &RenderOptions::default()))),
1929                "values" | "children" | "cn" => Some(AttrValue::Array(values.clone())),
1930                _ => None,
1931            },
1932            Node::TableRow(TableRow { values, .. }) => match attr {
1933                "value" => Some(AttrValue::String(values_to_string(values, &RenderOptions::default()))),
1934                "values" | "children" | "cn" => Some(AttrValue::Array(values.clone())),
1935                _ => None,
1936            },
1937            Node::Fragment(Fragment { values, .. }) => match attr {
1938                "value" => Some(AttrValue::String(values_to_string(values, &RenderOptions::default()))),
1939                "values" | "children" | "cn" => Some(AttrValue::Array(values.clone())),
1940                _ => None,
1941            },
1942            Node::Break(_) | Node::HorizontalRule(_) | Node::Empty => None,
1943        }
1944    }
1945
1946    /// Sets the value of the specified attribute for the node, if supported.
1947    pub fn set_attr(&mut self, attr: &str, value: impl Into<AttrValue>) {
1948        let value = value.into();
1949        let value_str = value.as_string();
1950
1951        match self {
1952            Node::Footnote(f) => {
1953                if attr == "ident" {
1954                    f.ident = value_str;
1955                }
1956            }
1957            Node::Html(h) => {
1958                if attr == "value" {
1959                    h.value = value_str;
1960                }
1961            }
1962            Node::Text(t) => {
1963                if attr == "value" {
1964                    t.value = value_str;
1965                }
1966            }
1967            Node::Code(c) => match attr {
1968                "value" => {
1969                    c.value = value_str;
1970                }
1971                "lang" | "language" => {
1972                    c.lang = if value_str.is_empty() { None } else { Some(value_str) };
1973                }
1974                "meta" => {
1975                    c.meta = if value_str.is_empty() { None } else { Some(value_str) };
1976                }
1977                "fence" => {
1978                    c.fence = match value {
1979                        AttrValue::Boolean(b) => b,
1980                        _ => value_str == "true",
1981                    };
1982                }
1983                _ => (),
1984            },
1985            Node::CodeInline(ci) => {
1986                if attr == "value" {
1987                    ci.value = value_str.into();
1988                }
1989            }
1990            Node::MathInline(mi) => {
1991                if attr == "value" {
1992                    mi.value = value_str.into();
1993                }
1994            }
1995            Node::Math(m) => {
1996                if attr == "value" {
1997                    m.value = value_str;
1998                }
1999            }
2000            Node::Yaml(y) => {
2001                if attr == "value" {
2002                    y.value = value_str;
2003                }
2004            }
2005            Node::Toml(t) => {
2006                if attr == "value" {
2007                    t.value = value_str;
2008                }
2009            }
2010            Node::Image(i) => match attr {
2011                "alt" => {
2012                    i.alt = value_str;
2013                }
2014                "url" => {
2015                    i.url = value_str;
2016                }
2017                "title" => {
2018                    i.title = if value_str.is_empty() { None } else { Some(value_str) };
2019                }
2020                _ => (),
2021            },
2022            Node::ImageRef(i) => match attr {
2023                "alt" => {
2024                    i.alt = value_str;
2025                }
2026                "ident" => {
2027                    i.ident = value_str;
2028                }
2029                "label" => {
2030                    i.label = if value_str.is_empty() { None } else { Some(value_str) };
2031                }
2032                _ => (),
2033            },
2034            Node::Link(l) => match attr {
2035                "url" => {
2036                    l.url = Url::new(value_str);
2037                }
2038                "title" => {
2039                    l.title = if value_str.is_empty() {
2040                        None
2041                    } else {
2042                        Some(Title::new(value_str))
2043                    };
2044                }
2045                _ => (),
2046            },
2047            Node::LinkRef(l) => match attr {
2048                "ident" => {
2049                    l.ident = value_str;
2050                }
2051                "label" => {
2052                    l.label = if value_str.is_empty() { None } else { Some(value_str) };
2053                }
2054                _ => (),
2055            },
2056            Node::FootnoteRef(f) => match attr {
2057                "ident" => {
2058                    f.ident = value_str;
2059                }
2060                "label" => {
2061                    f.label = if value_str.is_empty() { None } else { Some(value_str) };
2062                }
2063                _ => (),
2064            },
2065            Node::Definition(d) => match attr {
2066                "ident" => {
2067                    d.ident = value_str;
2068                }
2069                "url" => {
2070                    d.url = Url::new(value_str);
2071                }
2072                "title" => {
2073                    d.title = if value_str.is_empty() {
2074                        None
2075                    } else {
2076                        Some(Title::new(value_str))
2077                    };
2078                }
2079                "label" => {
2080                    d.label = if value_str.is_empty() { None } else { Some(value_str) };
2081                }
2082                _ => (),
2083            },
2084            Node::Heading(h) => match attr {
2085                "depth" | "level" => {
2086                    h.depth = match value {
2087                        AttrValue::Integer(i) => i as u8,
2088                        _ => value_str.parse::<u8>().unwrap_or(h.depth),
2089                    };
2090                }
2091                _ => (),
2092            },
2093            Node::List(l) => match attr {
2094                "index" => {
2095                    l.index = match value {
2096                        AttrValue::Integer(i) => i as usize,
2097                        _ => value_str.parse::<usize>().unwrap_or(l.index),
2098                    };
2099                }
2100                "level" => {
2101                    l.level = match value {
2102                        AttrValue::Integer(i) => i as u8,
2103                        _ => value_str.parse::<u8>().unwrap_or(l.level),
2104                    };
2105                }
2106                "ordered" => {
2107                    l.ordered = match value {
2108                        AttrValue::Boolean(b) => b,
2109                        _ => value_str == "true",
2110                    };
2111                }
2112                "checked" => {
2113                    l.checked = if value_str.is_empty() {
2114                        None
2115                    } else {
2116                        Some(match value {
2117                            AttrValue::Boolean(b) => b,
2118                            _ => value_str == "true",
2119                        })
2120                    };
2121                }
2122                _ => (),
2123            },
2124            Node::TableCell(c) => match attr {
2125                "column" => {
2126                    c.column = match value {
2127                        AttrValue::Integer(i) => i as usize,
2128                        _ => value_str.parse::<usize>().unwrap_or(c.column),
2129                    };
2130                }
2131                "row" => {
2132                    c.row = match value {
2133                        AttrValue::Integer(i) => i as usize,
2134                        _ => value_str.parse::<usize>().unwrap_or(c.row),
2135                    };
2136                }
2137                "last_cell_in_row" => {
2138                    c.last_cell_in_row = match value {
2139                        AttrValue::Boolean(b) => b,
2140                        _ => value_str == "true",
2141                    };
2142                }
2143                "last_cell_of_in_table" => {
2144                    c.last_cell_of_in_table = match value {
2145                        AttrValue::Boolean(b) => b,
2146                        _ => value_str == "true",
2147                    };
2148                }
2149                _ => (),
2150            },
2151            Node::TableHeader(th) => {
2152                if attr == "align" {
2153                    th.align = value_str
2154                        .split(',')
2155                        .map(|s| match s.trim() {
2156                            ":---" => TableAlignKind::Left,
2157                            "---:" => TableAlignKind::Right,
2158                            ":---:" => TableAlignKind::Center,
2159                            "---" => TableAlignKind::None,
2160                            _ => TableAlignKind::None,
2161                        })
2162                        .collect();
2163                }
2164            }
2165            Node::MdxFlowExpression(m) => {
2166                if attr == "value" {
2167                    m.value = value_str.into();
2168                }
2169            }
2170            Node::MdxTextExpression(m) => {
2171                if attr == "value" {
2172                    m.value = value_str.into();
2173                }
2174            }
2175            Node::MdxJsEsm(m) => {
2176                if attr == "value" {
2177                    m.value = value_str.into();
2178                }
2179            }
2180            Node::MdxJsxFlowElement(m) => {
2181                if attr == "name" {
2182                    m.name = if value_str.is_empty() { None } else { Some(value_str) };
2183                }
2184            }
2185            Node::MdxJsxTextElement(m) => {
2186                if attr == "name" {
2187                    m.name = if value_str.is_empty() {
2188                        None
2189                    } else {
2190                        Some(value_str.into())
2191                    };
2192                }
2193            }
2194            Node::Delete(_)
2195            | Node::Blockquote(_)
2196            | Node::Emphasis(_)
2197            | Node::Strong(_)
2198            | Node::TableRow(_)
2199            | Node::Break(_)
2200            | Node::HorizontalRule(_)
2201            | Node::Fragment(_)
2202            | Node::Empty => (),
2203        }
2204    }
2205
2206    pub(crate) fn from_mdast_node(node: mdast::Node) -> Vec<Node> {
2207        match node.clone() {
2208            mdast::Node::Root(root) => root
2209                .children
2210                .into_iter()
2211                .flat_map(Self::from_mdast_node)
2212                .collect::<Vec<_>>(),
2213            mdast::Node::ListItem(list_item) => list_item
2214                .children
2215                .into_iter()
2216                .flat_map(Self::from_mdast_node)
2217                .collect::<Vec<_>>(),
2218            mdast::Node::List(list) => Self::mdast_list_items(&list, 0),
2219            mdast::Node::Table(table) => table
2220                .children
2221                .iter()
2222                .enumerate()
2223                .flat_map(|(row, n)| {
2224                    if let mdast::Node::TableRow(table_row) = n {
2225                        itertools::concat(vec![
2226                            table_row
2227                                .children
2228                                .iter()
2229                                .enumerate()
2230                                .flat_map(|(column, node)| {
2231                                    if let mdast::Node::TableCell(_) = node {
2232                                        vec![Self::TableCell(TableCell {
2233                                            row,
2234                                            column,
2235                                            last_cell_in_row: table_row.children.len() - 1 == column,
2236                                            last_cell_of_in_table: table_row.children.len() - 1 == column
2237                                                && table.children.len() - 1 == row,
2238                                            values: Self::mdast_children_to_node(node.clone()),
2239                                            position: node.position().map(|p| p.clone().into()),
2240                                        })]
2241                                    } else {
2242                                        Vec::new()
2243                                    }
2244                                })
2245                                .collect(),
2246                            if row == 0 {
2247                                vec![Self::TableHeader(TableHeader {
2248                                    align: table.align.iter().map(|a| (*a).into()).collect::<Vec<_>>(),
2249                                    position: n.position().map(|p| Position {
2250                                        start: Point {
2251                                            line: p.start.line + 1,
2252                                            column: 1,
2253                                        },
2254                                        end: Point {
2255                                            line: p.start.line + 1,
2256                                            column: 1,
2257                                        },
2258                                    }),
2259                                })]
2260                            } else {
2261                                Vec::new()
2262                            },
2263                        ])
2264                    } else {
2265                        Vec::new()
2266                    }
2267                })
2268                .collect(),
2269            mdast::Node::Code(mdast::Code {
2270                value,
2271                position,
2272                lang,
2273                meta,
2274                ..
2275            }) => match lang {
2276                Some(lang) => {
2277                    vec![Self::Code(Code {
2278                        value,
2279                        lang: Some(lang),
2280                        position: position.map(|p| p.clone().into()),
2281                        meta,
2282                        fence: true,
2283                    })]
2284                }
2285                None => {
2286                    let line_count = position
2287                        .as_ref()
2288                        .map(|p| p.end.line - p.start.line + 1)
2289                        .unwrap_or_default();
2290                    let fence = value.lines().count() != line_count;
2291
2292                    vec![Self::Code(Code {
2293                        value,
2294                        lang,
2295                        position: position.map(|p| p.clone().into()),
2296                        meta,
2297                        fence,
2298                    })]
2299                }
2300            },
2301            mdast::Node::Blockquote(mdast::Blockquote { position, .. }) => {
2302                vec![Self::Blockquote(Blockquote {
2303                    values: Self::mdast_children_to_node(node),
2304                    position: position.map(|p| p.clone().into()),
2305                })]
2306            }
2307            mdast::Node::Definition(mdast::Definition {
2308                url,
2309                title,
2310                identifier,
2311                label,
2312                position,
2313                ..
2314            }) => {
2315                vec![Self::Definition(Definition {
2316                    ident: identifier,
2317                    url: Url(url),
2318                    label,
2319                    title: title.map(Title),
2320                    position: position.map(|p| p.clone().into()),
2321                })]
2322            }
2323            mdast::Node::Heading(mdast::Heading { depth, position, .. }) => {
2324                vec![Self::Heading(Heading {
2325                    values: Self::mdast_children_to_node(node),
2326                    depth,
2327                    position: position.map(|p| p.clone().into()),
2328                })]
2329            }
2330            mdast::Node::Break(mdast::Break { position }) => {
2331                vec![Self::Break(Break {
2332                    position: position.map(|p| p.clone().into()),
2333                })]
2334            }
2335            mdast::Node::Delete(mdast::Delete { position, .. }) => {
2336                vec![Self::Delete(Delete {
2337                    values: Self::mdast_children_to_node(node),
2338                    position: position.map(|p| p.clone().into()),
2339                })]
2340            }
2341            mdast::Node::Emphasis(mdast::Emphasis { position, .. }) => {
2342                vec![Self::Emphasis(Emphasis {
2343                    values: Self::mdast_children_to_node(node),
2344                    position: position.map(|p| p.clone().into()),
2345                })]
2346            }
2347            mdast::Node::Strong(mdast::Strong { position, .. }) => {
2348                vec![Self::Strong(Strong {
2349                    values: Self::mdast_children_to_node(node),
2350                    position: position.map(|p| p.clone().into()),
2351                })]
2352            }
2353            mdast::Node::ThematicBreak(mdast::ThematicBreak { position, .. }) => {
2354                vec![Self::HorizontalRule(HorizontalRule {
2355                    position: position.map(|p| p.clone().into()),
2356                })]
2357            }
2358            mdast::Node::Html(mdast::Html { value, position }) => {
2359                vec![Self::Html(Html {
2360                    value,
2361                    position: position.map(|p| p.clone().into()),
2362                })]
2363            }
2364            mdast::Node::Yaml(mdast::Yaml { value, position }) => {
2365                vec![Self::Yaml(Yaml {
2366                    value,
2367                    position: position.map(|p| p.clone().into()),
2368                })]
2369            }
2370            mdast::Node::Toml(mdast::Toml { value, position }) => {
2371                vec![Self::Toml(Toml {
2372                    value,
2373                    position: position.map(|p| p.clone().into()),
2374                })]
2375            }
2376            mdast::Node::Image(mdast::Image {
2377                alt,
2378                url,
2379                title,
2380                position,
2381            }) => {
2382                vec![Self::Image(Image {
2383                    alt,
2384                    url,
2385                    title,
2386                    position: position.map(|p| p.clone().into()),
2387                })]
2388            }
2389            mdast::Node::ImageReference(mdast::ImageReference {
2390                alt,
2391                identifier,
2392                label,
2393                position,
2394                ..
2395            }) => {
2396                vec![Self::ImageRef(ImageRef {
2397                    alt,
2398                    ident: identifier,
2399                    label,
2400                    position: position.map(|p| p.clone().into()),
2401                })]
2402            }
2403            mdast::Node::InlineCode(mdast::InlineCode { value, position, .. }) => {
2404                vec![Self::CodeInline(CodeInline {
2405                    value: value.into(),
2406                    position: position.map(|p| p.clone().into()),
2407                })]
2408            }
2409            mdast::Node::InlineMath(mdast::InlineMath { value, position }) => {
2410                vec![Self::MathInline(MathInline {
2411                    value: value.into(),
2412                    position: position.map(|p| p.clone().into()),
2413                })]
2414            }
2415            mdast::Node::Link(mdast::Link {
2416                title,
2417                url,
2418                position,
2419                children,
2420                ..
2421            }) => {
2422                vec![Self::Link(Link {
2423                    url: Url(url),
2424                    title: title.map(Title),
2425                    values: children.into_iter().flat_map(Self::from_mdast_node).collect::<Vec<_>>(),
2426                    position: position.map(|p| p.clone().into()),
2427                })]
2428            }
2429            mdast::Node::LinkReference(mdast::LinkReference {
2430                identifier,
2431                label,
2432                position,
2433                children,
2434                ..
2435            }) => {
2436                vec![Self::LinkRef(LinkRef {
2437                    ident: identifier,
2438                    values: children.into_iter().flat_map(Self::from_mdast_node).collect::<Vec<_>>(),
2439                    label,
2440                    position: position.map(|p| p.clone().into()),
2441                })]
2442            }
2443            mdast::Node::Math(mdast::Math { value, position, .. }) => {
2444                vec![Self::Math(Math {
2445                    value,
2446                    position: position.map(|p| p.clone().into()),
2447                })]
2448            }
2449            mdast::Node::FootnoteDefinition(mdast::FootnoteDefinition {
2450                identifier,
2451                position,
2452                children,
2453                ..
2454            }) => {
2455                vec![Self::Footnote(Footnote {
2456                    ident: identifier,
2457                    values: children.into_iter().flat_map(Self::from_mdast_node).collect::<Vec<_>>(),
2458                    position: position.map(|p| p.clone().into()),
2459                })]
2460            }
2461            mdast::Node::FootnoteReference(mdast::FootnoteReference {
2462                identifier,
2463                label,
2464                position,
2465                ..
2466            }) => {
2467                vec![Self::FootnoteRef(FootnoteRef {
2468                    ident: identifier,
2469                    label,
2470                    position: position.map(|p| p.clone().into()),
2471                })]
2472            }
2473            mdast::Node::MdxFlowExpression(mdx) => {
2474                vec![Self::MdxFlowExpression(MdxFlowExpression {
2475                    value: mdx.value.into(),
2476                    position: mdx.position.map(|position| position.into()),
2477                })]
2478            }
2479            mdast::Node::MdxJsxFlowElement(mdx) => {
2480                vec![Self::MdxJsxFlowElement(MdxJsxFlowElement {
2481                    children: mdx
2482                        .children
2483                        .into_iter()
2484                        .flat_map(Self::from_mdast_node)
2485                        .collect::<Vec<_>>(),
2486                    position: mdx.position.map(|p| p.clone().into()),
2487                    name: mdx.name,
2488                    attributes: mdx
2489                        .attributes
2490                        .iter()
2491                        .map(|attr| match attr {
2492                            mdast::AttributeContent::Expression(mdast::MdxJsxExpressionAttribute { value, .. }) => {
2493                                MdxAttributeContent::Expression(value.into())
2494                            }
2495                            mdast::AttributeContent::Property(mdast::MdxJsxAttribute { value, name, .. }) => {
2496                                MdxAttributeContent::Property(MdxJsxAttribute {
2497                                    name: name.into(),
2498                                    value: value.as_ref().map(|value| match value {
2499                                        mdast::AttributeValue::Literal(value) => {
2500                                            MdxAttributeValue::Literal(value.into())
2501                                        }
2502                                        mdast::AttributeValue::Expression(mdast::AttributeValueExpression {
2503                                            value,
2504                                            ..
2505                                        }) => MdxAttributeValue::Expression(value.into()),
2506                                    }),
2507                                })
2508                            }
2509                        })
2510                        .collect(),
2511                })]
2512            }
2513            mdast::Node::MdxJsxTextElement(mdx) => {
2514                vec![Self::MdxJsxTextElement(MdxJsxTextElement {
2515                    children: mdx
2516                        .children
2517                        .into_iter()
2518                        .flat_map(Self::from_mdast_node)
2519                        .collect::<Vec<_>>(),
2520                    position: mdx.position.map(|p| p.clone().into()),
2521                    name: mdx.name.map(|name| name.into()),
2522                    attributes: mdx
2523                        .attributes
2524                        .iter()
2525                        .map(|attr| match attr {
2526                            mdast::AttributeContent::Expression(mdast::MdxJsxExpressionAttribute { value, .. }) => {
2527                                MdxAttributeContent::Expression(value.into())
2528                            }
2529                            mdast::AttributeContent::Property(mdast::MdxJsxAttribute { value, name, .. }) => {
2530                                MdxAttributeContent::Property(MdxJsxAttribute {
2531                                    name: name.into(),
2532                                    value: value.as_ref().map(|value| match value {
2533                                        mdast::AttributeValue::Literal(value) => {
2534                                            MdxAttributeValue::Literal(value.into())
2535                                        }
2536                                        mdast::AttributeValue::Expression(mdast::AttributeValueExpression {
2537                                            value,
2538                                            ..
2539                                        }) => MdxAttributeValue::Expression(value.into()),
2540                                    }),
2541                                })
2542                            }
2543                        })
2544                        .collect(),
2545                })]
2546            }
2547            mdast::Node::MdxTextExpression(mdx) => {
2548                vec![Self::MdxTextExpression(MdxTextExpression {
2549                    value: mdx.value.into(),
2550                    position: mdx.position.map(|position| position.into()),
2551                })]
2552            }
2553            mdast::Node::MdxjsEsm(mdx) => {
2554                vec![Self::MdxJsEsm(MdxJsEsm {
2555                    value: mdx.value.into(),
2556                    position: mdx.position.map(|position| position.into()),
2557                })]
2558            }
2559            mdast::Node::Text(mdast::Text { position, value, .. }) => {
2560                vec![Self::Text(Text {
2561                    value,
2562                    position: position.map(|p| p.clone().into()),
2563                })]
2564            }
2565            mdast::Node::Paragraph(mdast::Paragraph { children, .. }) => {
2566                children.into_iter().flat_map(Self::from_mdast_node).collect::<Vec<_>>()
2567            }
2568            _ => Vec::new(),
2569        }
2570    }
2571
2572    fn mdast_children_to_node(node: mdast::Node) -> Vec<Node> {
2573        node.children()
2574            .map(|children| {
2575                children
2576                    .iter()
2577                    .flat_map(|v| Self::from_mdast_node(v.clone()))
2578                    .collect::<Vec<_>>()
2579            })
2580            .unwrap_or_else(|| vec![EMPTY_NODE])
2581    }
2582
2583    fn mdast_list_items(list: &mdast::List, level: Level) -> Vec<Node> {
2584        list.children
2585            .iter()
2586            .flat_map(|n| {
2587                if let mdast::Node::ListItem(list_item) = n {
2588                    let values = Self::from_mdast_node(n.clone())
2589                        .into_iter()
2590                        .filter(|value| !matches!(value, Self::List(_)))
2591                        .collect::<Vec<_>>();
2592                    let position = if values.is_empty() {
2593                        n.position().map(|p| p.clone().into())
2594                    } else {
2595                        let first_pos = values.first().and_then(|v| v.position());
2596                        let last_pos = values.last().and_then(|v| v.position());
2597                        match (first_pos, last_pos) {
2598                            (Some(start), Some(end)) => Some(Position {
2599                                start: start.start.clone(),
2600                                end: end.end.clone(),
2601                            }),
2602                            _ => n.position().map(|p| p.clone().into()),
2603                        }
2604                    };
2605
2606                    itertools::concat(vec![
2607                        vec![Self::List(List {
2608                            level,
2609                            index: 0,
2610                            ordered: list.ordered,
2611                            checked: list_item.checked,
2612                            values,
2613                            position,
2614                        })],
2615                        list_item
2616                            .children
2617                            .iter()
2618                            .flat_map(|node| {
2619                                if let mdast::Node::List(sub_list) = node {
2620                                    Self::mdast_list_items(sub_list, level + 1)
2621                                } else if let mdast::Node::ListItem(list_item) = node {
2622                                    let values = Self::from_mdast_node(n.clone())
2623                                        .into_iter()
2624                                        .filter(|value| !matches!(value, Self::List(_)))
2625                                        .collect::<Vec<_>>();
2626                                    let position = if values.is_empty() {
2627                                        n.position().map(|p| p.clone().into())
2628                                    } else {
2629                                        let first_pos = values.first().and_then(|v| v.position());
2630                                        let last_pos = values.last().and_then(|v| v.position());
2631                                        match (first_pos, last_pos) {
2632                                            (Some(start), Some(end)) => Some(Position {
2633                                                start: start.start.clone(),
2634                                                end: end.end.clone(),
2635                                            }),
2636                                            _ => n.position().map(|p| p.clone().into()),
2637                                        }
2638                                    };
2639                                    vec![Self::List(List {
2640                                        level: level + 1,
2641                                        index: 0,
2642                                        ordered: list.ordered,
2643                                        checked: list_item.checked,
2644                                        values,
2645                                        position,
2646                                    })]
2647                                } else {
2648                                    Vec::new()
2649                                }
2650                            })
2651                            .collect(),
2652                    ])
2653                } else if let mdast::Node::List(sub_list) = n {
2654                    Self::mdast_list_items(sub_list, level + 1)
2655                } else {
2656                    Vec::new()
2657                }
2658            })
2659            .enumerate()
2660            .filter_map(|(i, node)| match node {
2661                Self::List(List {
2662                    level,
2663                    index: _,
2664                    ordered,
2665                    checked,
2666                    values,
2667                    position,
2668                }) => Some(Self::List(List {
2669                    level,
2670                    index: i,
2671                    ordered,
2672                    checked,
2673                    values,
2674                    position,
2675                })),
2676                _ => None,
2677            })
2678            .collect()
2679    }
2680
2681    fn mdx_attribute_content_to_string(attr: MdxAttributeContent) -> SmolStr {
2682        match attr {
2683            MdxAttributeContent::Expression(value) => format!("{{{}}}", value).into(),
2684            MdxAttributeContent::Property(property) => match property.value {
2685                Some(value) => match value {
2686                    MdxAttributeValue::Expression(value) => format!("{}={{{}}}", property.name, value).into(),
2687                    MdxAttributeValue::Literal(literal) => format!("{}=\"{}\"", property.name, literal).into(),
2688                },
2689                None => property.name,
2690            },
2691        }
2692    }
2693}
2694
2695#[inline(always)]
2696fn values_to_string(values: &[Node], options: &RenderOptions) -> String {
2697    let mut pre_position: Option<Position> = None;
2698    values
2699        .iter()
2700        .map(|value| {
2701            if let Some(pos) = value.position() {
2702                let new_line_count = pre_position
2703                    .as_ref()
2704                    .map(|p: &Position| pos.start.line - p.end.line)
2705                    .unwrap_or_default();
2706
2707                let space = if new_line_count > 0
2708                    && pre_position
2709                        .as_ref()
2710                        .map(|p| pos.start.line > p.end.line)
2711                        .unwrap_or_default()
2712                {
2713                    " ".repeat(pos.start.column.saturating_sub(1))
2714                } else {
2715                    "".to_string()
2716                };
2717
2718                pre_position = Some(pos);
2719
2720                if space.is_empty() {
2721                    format!("{}{}", "\n".repeat(new_line_count), value.to_string_with(options))
2722                } else {
2723                    format!(
2724                        "{}{}",
2725                        "\n".repeat(new_line_count),
2726                        value
2727                            .to_string_with(options)
2728                            .lines()
2729                            .map(|line| format!("{}{}", space, line))
2730                            .join("\n")
2731                    )
2732                }
2733            } else {
2734                pre_position = None;
2735                value.to_string_with(options)
2736            }
2737        })
2738        .collect::<String>()
2739}
2740
2741#[inline(always)]
2742fn values_to_value(values: Vec<Node>) -> String {
2743    values.iter().map(|value| value.value()).collect::<String>()
2744}
2745
2746#[cfg(test)]
2747mod tests {
2748    use super::*;
2749    use rstest::rstest;
2750
2751    #[rstest]
2752    #[case::text(Node::Text(Text{value: "".to_string(), position: None}),
2753           "test".to_string(),
2754           Node::Text(Text{value: "test".to_string(), position: None }))]
2755    #[case::blockquote(Node::Blockquote(Blockquote{values: vec!["test".to_string().into()], position: None }),
2756           "test".to_string(),
2757           Node::Blockquote(Blockquote{values: vec!["test".to_string().into()], position: None }))]
2758    #[case::delete(Node::Delete(Delete{values: vec!["test".to_string().into()], position: None }),
2759           "test".to_string(),
2760           Node::Delete(Delete{values: vec!["test".to_string().into()], position: None }))]
2761    #[case::emphasis(Node::Emphasis(Emphasis{values: vec!["test".to_string().into()], position: None }),
2762           "test".to_string(),
2763           Node::Emphasis(Emphasis{values: vec!["test".to_string().into()], position: None }))]
2764    #[case::strong(Node::Strong(Strong{values: vec!["test".to_string().into()], position: None }),
2765           "test".to_string(),
2766           Node::Strong(Strong{values: vec!["test".to_string().into()], position: None }))]
2767    #[case::heading(Node::Heading(Heading {depth: 1, values: vec!["test".to_string().into()], position: None }),
2768           "test".to_string(),
2769           Node::Heading(Heading{depth: 1, values: vec!["test".to_string().into()], position: None }))]
2770    #[case::link(Node::Link(Link {url: Url::new("test".to_string()), values: Vec::new(), title: None, position: None }),
2771           "test".to_string(),
2772           Node::Link(Link{url: Url::new("test".to_string()), values: Vec::new(), title: None, position: None }))]
2773    #[case::image(Node::Image(Image {alt: "test".to_string(), url: "test".to_string(), title: None, position: None }),
2774           "test".to_string(),
2775           Node::Image(Image{alt: "test".to_string(), url: "test".to_string(), title: None, position: None }))]
2776    #[case::code(Node::Code(Code {value: "test".to_string(), lang: None, fence: true, meta: None, position: None }),
2777           "test".to_string(),
2778           Node::Code(Code{value: "test".to_string(), lang: None, fence: true, meta: None, position: None }))]
2779    #[case::footnote_ref(Node::FootnoteRef(FootnoteRef {ident: "test".to_string(), label: None, position: None }),
2780           "test".to_string(),
2781           Node::FootnoteRef(FootnoteRef{ident: "test".to_string(), label: Some("test".to_string()), position: None }))]
2782    #[case::footnote(Node::Footnote(Footnote {ident: "test".to_string(), values: Vec::new(), position: None }),
2783           "test".to_string(),
2784           Node::Footnote(Footnote{ident: "test".to_string(), values: Vec::new(), position: None }))]
2785    #[case::list(Node::List(List{index: 0, level: 0, checked: None, ordered: false, values: vec!["test".to_string().into()], position: None }),
2786           "test".to_string(),
2787           Node::List(List{index: 0, level: 0, checked: None, ordered: false, values: vec!["test".to_string().into()], position: None }))]
2788    #[case::list(Node::List(List{index: 1, level: 1, checked: Some(true), ordered: false, values: vec!["test".to_string().into()], position: None }),
2789           "test".to_string(),
2790           Node::List(List{index: 1, level: 1, checked: Some(true), ordered: false, values: vec!["test".to_string().into()], position: None }))]
2791    #[case::list(Node::List(List{index: 2, level: 2, checked: Some(false), ordered: false, values: vec!["test".to_string().into()], position: None }),
2792           "test".to_string(),
2793           Node::List(List{index: 2, level: 2, checked: Some(false), ordered: false, values: vec!["test".to_string().into()], position: None }))]
2794    #[case::code_inline(Node::CodeInline(CodeInline{ value: "t".into(), position: None }),
2795           "test".to_string(),
2796           Node::CodeInline(CodeInline{ value: "test".into(), position: None }))]
2797    #[case::math_inline(Node::MathInline(MathInline{ value: "t".into(), position: None }),
2798           "test".to_string(),
2799           Node::MathInline(MathInline{ value: "test".into(), position: None }))]
2800    #[case::toml(Node::Toml(Toml{ value: "t".to_string(), position: None }),
2801           "test".to_string(),
2802           Node::Toml(Toml{ value: "test".to_string(), position: None }))]
2803    #[case::yaml(Node::Yaml(Yaml{ value: "t".to_string(), position: None }),
2804           "test".to_string(),
2805           Node::Yaml(Yaml{ value: "test".to_string(), position: None }))]
2806    #[case::html(Node::Html(Html{ value: "t".to_string(), position: None }),
2807           "test".to_string(),
2808           Node::Html(Html{ value: "test".to_string(), position: None }))]
2809    #[case::table_row(Node::TableRow(TableRow{ values: vec![
2810                        Node::TableCell(TableCell{values: vec!["test1".to_string().into()], row:0, column:1, last_cell_in_row: false, last_cell_of_in_table: false, position: None}),
2811                        Node::TableCell(TableCell{values: vec!["test2".to_string().into()], row:0, column:2, last_cell_in_row: true, last_cell_of_in_table: false, position: None})
2812                    ]
2813                    , position: None }),
2814           "test3,test4".to_string(),
2815           Node::TableRow(TableRow{ values: vec![
2816                        Node::TableCell(TableCell{values: vec!["test3".to_string().into()], row:0, column:1, last_cell_in_row: false, last_cell_of_in_table: false, position: None}),
2817                        Node::TableCell(TableCell{values: vec!["test4".to_string().into()], row:0, column:2, last_cell_in_row: true, last_cell_of_in_table: false, position: None})
2818                    ]
2819                    , position: None }))]
2820    #[case::table_cell(Node::TableCell(TableCell{values: vec!["test1".to_string().into()], row:0, column:1, last_cell_in_row: false, last_cell_of_in_table: false, position: None}),
2821            "test2".to_string(),
2822            Node::TableCell(TableCell{values: vec!["test2".to_string().into()], row:0, column:1, last_cell_in_row: false, last_cell_of_in_table: false, position: None}),)]
2823    #[case::link_ref(Node::LinkRef(LinkRef{ident: "test2".to_string(), values: vec!["value".to_string().into()], label: Some("test2".to_string()), position: None}),
2824            "test2".to_string(),
2825            Node::LinkRef(LinkRef{ident: "test2".to_string(), values: vec!["value".to_string().into()], label: Some("test2".to_string()), position: None}),)]
2826    #[case::image_ref(Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "test1".to_string(), label: None, position: None}),
2827            "test2".to_string(),
2828            Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "test2".to_string(), label: Some("test2".to_string()), position: None}),)]
2829    #[case::definition(Node::Definition(Definition{ url: Url::new("url".to_string()), title: None, ident: "test1".to_string(), label: None, position: None}),
2830            "test2".to_string(),
2831            Node::Definition(Definition{url: Url::new("test2".to_string()), title: None, ident: "test1".to_string(), label: None, position: None}),)]
2832    #[case::break_(Node::Break(Break{ position: None}),
2833            "test".to_string(),
2834            Node::Break(Break{position: None}))]
2835    #[case::horizontal_rule(Node::HorizontalRule(HorizontalRule{ position: None}),
2836            "test".to_string(),
2837            Node::HorizontalRule(HorizontalRule{position: None}))]
2838    #[case::mdx_flow_expression(Node::MdxFlowExpression(MdxFlowExpression{value: "test".into(), position: None}),
2839           "updated".to_string(),
2840           Node::MdxFlowExpression(MdxFlowExpression{value: "updated".into(), position: None}))]
2841    #[case::mdx_text_expression(Node::MdxTextExpression(MdxTextExpression{value: "test".into(), position: None}),
2842           "updated".to_string(),
2843           Node::MdxTextExpression(MdxTextExpression{value: "updated".into(), position: None}))]
2844    #[case::mdx_js_esm(Node::MdxJsEsm(MdxJsEsm{value: "test".into(), position: None}),
2845           "updated".to_string(),
2846           Node::MdxJsEsm(MdxJsEsm{value: "updated".into(), position: None}))]
2847    #[case(Node::MdxJsxFlowElement(MdxJsxFlowElement{
2848            name: Some("div".to_string()),
2849            attributes: Vec::new(),
2850            children: vec!["test".to_string().into()],
2851            position: None
2852        }),
2853        "updated".to_string(),
2854        Node::MdxJsxFlowElement(MdxJsxFlowElement{
2855            name: Some("div".to_string()),
2856            attributes: Vec::new(),
2857            children: vec!["updated".to_string().into()],
2858            position: None
2859        }))]
2860    #[case::mdx_jsx_text_element(Node::MdxJsxTextElement(MdxJsxTextElement{
2861            name: Some("span".into()),
2862            attributes: Vec::new(),
2863            children: vec!["test".to_string().into()],
2864            position: None
2865        }),
2866        "updated".to_string(),
2867        Node::MdxJsxTextElement(MdxJsxTextElement{
2868            name: Some("span".into()),
2869            attributes: Vec::new(),
2870            children: vec!["updated".to_string().into()],
2871            position: None
2872        }))]
2873    #[case(Node::Math(Math{ value: "x^2".to_string(), position: None }),
2874           "test".to_string(),
2875           Node::Math(Math{ value: "test".to_string(), position: None }))]
2876    fn test_with_value(#[case] node: Node, #[case] input: String, #[case] expected: Node) {
2877        assert_eq!(node.with_value(input.as_str()), expected);
2878    }
2879
2880    #[rstest]
2881    #[case(Node::Blockquote(Blockquote{values: vec![
2882        Node::Text(Text{value: "first".to_string(), position: None}),
2883        Node::Text(Text{value: "second".to_string(), position: None})
2884    ], position: None}),
2885        "new",
2886        0,
2887        Node::Blockquote(Blockquote{values: vec![
2888            Node::Text(Text{value: "new".to_string(), position: None}),
2889            Node::Text(Text{value: "second".to_string(), position: None})
2890        ], position: None}))]
2891    #[case(Node::Blockquote(Blockquote{values: vec![
2892        Node::Text(Text{value: "first".to_string(), position: None}),
2893        Node::Text(Text{value: "second".to_string(), position: None})
2894    ], position: None}),
2895        "new",
2896        1,
2897        Node::Blockquote(Blockquote{values: vec![
2898            Node::Text(Text{value: "first".to_string(), position: None}),
2899            Node::Text(Text{value: "new".to_string(), position: None})
2900        ], position: None}))]
2901    #[case(Node::Delete(Delete{values: vec![
2902        Node::Text(Text{value: "first".to_string(), position: None}),
2903        Node::Text(Text{value: "second".to_string(), position: None})
2904    ], position: None}),
2905        "new",
2906        0,
2907        Node::Delete(Delete{values: vec![
2908            Node::Text(Text{value: "new".to_string(), position: None}),
2909            Node::Text(Text{value: "second".to_string(), position: None})
2910        ], position: None}))]
2911    #[case(Node::Emphasis(Emphasis{values: vec![
2912        Node::Text(Text{value: "first".to_string(), position: None}),
2913        Node::Text(Text{value: "second".to_string(), position: None})
2914    ], position: None}),
2915        "new",
2916        1,
2917        Node::Emphasis(Emphasis{values: vec![
2918            Node::Text(Text{value: "first".to_string(), position: None}),
2919            Node::Text(Text{value: "new".to_string(), position: None})
2920        ], position: None}))]
2921    #[case(Node::Strong(Strong{values: vec![
2922        Node::Text(Text{value: "first".to_string(), position: None}),
2923        Node::Text(Text{value: "second".to_string(), position: None})
2924    ], position: None}),
2925        "new",
2926        0,
2927        Node::Strong(Strong{values: vec![
2928            Node::Text(Text{value: "new".to_string(), position: None}),
2929            Node::Text(Text{value: "second".to_string(), position: None})
2930        ], position: None}))]
2931    #[case(Node::Heading(Heading{depth: 1, values: vec![
2932        Node::Text(Text{value: "first".to_string(), position: None}),
2933        Node::Text(Text{value: "second".to_string(), position: None})
2934    ], position: None}),
2935        "new",
2936        1,
2937        Node::Heading(Heading{depth: 1, values: vec![
2938            Node::Text(Text{value: "first".to_string(), position: None}),
2939            Node::Text(Text{value: "new".to_string(), position: None})
2940        ], position: None}))]
2941    #[case(Node::List(List{index: 0, level: 0, checked: None, ordered: false, values: vec![
2942        Node::Text(Text{value: "first".to_string(), position: None}),
2943        Node::Text(Text{value: "second".to_string(), position: None})
2944    ], position: None}),
2945        "new",
2946        0,
2947        Node::List(List{index: 0, level: 0, checked: None, ordered: false,  values: vec![
2948            Node::Text(Text{value: "new".to_string(), position: None}),
2949            Node::Text(Text{value: "second".to_string(), position: None})
2950        ], position: None}))]
2951    #[case(Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![
2952        Node::Text(Text{value: "first".to_string(), position: None}),
2953        Node::Text(Text{value: "second".to_string(), position: None})
2954    ], position: None}),
2955        "new",
2956        1,
2957        Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![
2958            Node::Text(Text{value: "first".to_string(), position: None}),
2959            Node::Text(Text{value: "new".to_string(), position: None})
2960        ], position: None}))]
2961    #[case(Node::Text(Text{value: "plain text".to_string(), position: None}),
2962        "new",
2963        0,
2964        Node::Text(Text{value: "plain text".to_string(), position: None}))]
2965    #[case(Node::Code(Code{value: "code".to_string(), lang: None, fence: true, meta: None, position: None}),
2966        "new",
2967        0,
2968        Node::Code(Code{value: "code".to_string(), lang: None, fence: true, meta: None, position: None}))]
2969    #[case(Node::List(List{index: 0, level: 1, checked: Some(true), ordered: false, values: vec![
2970        Node::Text(Text{value: "first".to_string(), position: None})
2971    ], position: None}),
2972        "new",
2973        0,
2974        Node::List(List{index: 0, level: 1, checked: Some(true), ordered: false, values: vec![
2975            Node::Text(Text{value: "new".to_string(), position: None})
2976        ], position: None}))]
2977    #[case(Node::List(List{index: 0, level: 1, checked: None, ordered: false, values: vec![
2978        Node::Text(Text{value: "first".to_string(), position: None})
2979    ], position: None}),
2980        "new",
2981        2,
2982        Node::List(List{index: 0, level: 1, checked: None, ordered: false, values: vec![
2983            Node::Text(Text{value: "first".to_string(), position: None})
2984        ], position: None}))]
2985    #[case::link_ref(Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec![
2986            Node::Text(Text{value: "first".to_string(), position: None}),
2987            Node::Text(Text{value: "second".to_string(), position: None})
2988        ], label: None, position: None}), "new", 0, Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec![
2989            Node::Text(Text{value: "new".to_string(), position: None}),
2990            Node::Text(Text{value: "second".to_string(), position: None})
2991        ], label: None, position: None}))]
2992    #[case(Node::MdxJsxFlowElement(MdxJsxFlowElement{
2993            name: Some("div".to_string()),
2994            attributes: Vec::new(),
2995            children: vec![
2996                Node::Text(Text{value: "first".to_string(), position: None}),
2997                Node::Text(Text{value: "second".to_string(), position: None})
2998            ],
2999            position: None
3000        }),
3001        "new",
3002        0,
3003        Node::MdxJsxFlowElement(MdxJsxFlowElement{
3004            name: Some("div".to_string()),
3005            attributes: Vec::new(),
3006            children: vec![
3007                Node::Text(Text{value: "new".to_string(), position: None}),
3008                Node::Text(Text{value: "second".to_string(), position: None})
3009            ],
3010            position: None
3011        }))]
3012    #[case(Node::MdxJsxFlowElement(MdxJsxFlowElement{
3013            name: Some("div".to_string()),
3014            attributes: Vec::new(),
3015            children: vec![
3016                Node::Text(Text{value: "first".to_string(), position: None}),
3017                Node::Text(Text{value: "second".to_string(), position: None})
3018            ],
3019            position: None
3020        }),
3021        "new",
3022        1,
3023        Node::MdxJsxFlowElement(MdxJsxFlowElement{
3024            name: Some("div".to_string()),
3025            attributes: Vec::new(),
3026            children: vec![
3027                Node::Text(Text{value: "first".to_string(), position: None}),
3028                Node::Text(Text{value: "new".to_string(), position: None})
3029            ],
3030            position: None
3031        }))]
3032    #[case(Node::MdxJsxTextElement(MdxJsxTextElement{
3033            name: Some("span".into()),
3034            attributes: Vec::new(),
3035            children: vec![
3036                Node::Text(Text{value: "first".to_string(), position: None}),
3037                Node::Text(Text{value: "second".to_string(), position: None})
3038            ],
3039            position: None
3040        }),
3041        "new",
3042        0,
3043        Node::MdxJsxTextElement(MdxJsxTextElement{
3044            name: Some("span".into()),
3045            attributes: Vec::new(),
3046            children: vec![
3047                Node::Text(Text{value: "new".to_string(), position: None}),
3048                Node::Text(Text{value: "second".to_string(), position: None})
3049            ],
3050            position: None
3051        }))]
3052    #[case(Node::MdxJsxTextElement(MdxJsxTextElement{
3053            name: Some("span".into()),
3054            attributes: Vec::new(),
3055            children: vec![
3056                Node::Text(Text{value: "first".to_string(), position: None}),
3057                Node::Text(Text{value: "second".to_string(), position: None})
3058            ],
3059            position: None
3060        }),
3061        "new",
3062        1,
3063        Node::MdxJsxTextElement(MdxJsxTextElement{
3064            name: Some("span".into()),
3065            attributes: Vec::new(),
3066            children: vec![
3067                Node::Text(Text{value: "first".to_string(), position: None}),
3068                Node::Text(Text{value: "new".to_string(), position: None})
3069            ],
3070            position: None
3071        }))]
3072    fn test_with_children_value(#[case] node: Node, #[case] value: &str, #[case] index: usize, #[case] expected: Node) {
3073        assert_eq!(node.with_children_value(value, index), expected);
3074    }
3075
3076    #[rstest]
3077    #[case(Node::Text(Text{value: "test".to_string(), position: None }),
3078           "test".to_string())]
3079    #[case(Node::List(List{index: 0, level: 2, checked: None, ordered: false, values: vec!["test".to_string().into()], position: None}),
3080           "    - test".to_string())]
3081    fn test_display(#[case] node: Node, #[case] expected: String) {
3082        assert_eq!(node.to_string_with(&RenderOptions::default()), expected);
3083    }
3084
3085    #[rstest]
3086    #[case(Node::Text(Text{value: "test".to_string(), position: None}), true)]
3087    #[case(Node::CodeInline(CodeInline{value: "test".into(), position: None}), false)]
3088    #[case(Node::MathInline(MathInline{value: "test".into(), position: None}), false)]
3089    fn test_is_text(#[case] node: Node, #[case] expected: bool) {
3090        assert_eq!(node.is_text(), expected);
3091    }
3092
3093    #[rstest]
3094    #[case(Node::CodeInline(CodeInline{value: "test".into(), position: None}), true)]
3095    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3096    fn test_is_inline_code(#[case] node: Node, #[case] expected: bool) {
3097        assert_eq!(node.is_inline_code(), expected);
3098    }
3099
3100    #[rstest]
3101    #[case(Node::MathInline(MathInline{value: "test".into(), position: None}), true)]
3102    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3103    fn test_is_inline_math(#[case] node: Node, #[case] expected: bool) {
3104        assert_eq!(node.is_inline_math(), expected);
3105    }
3106
3107    #[rstest]
3108    #[case(Node::Strong(Strong{values: vec!["test".to_string().into()], position: None}), true)]
3109    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3110    fn test_is_strong(#[case] node: Node, #[case] expected: bool) {
3111        assert_eq!(node.is_strong(), expected);
3112    }
3113
3114    #[rstest]
3115    #[case(Node::Delete(Delete{values: vec!["test".to_string().into()], position: None}), true)]
3116    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3117    fn test_is_delete(#[case] node: Node, #[case] expected: bool) {
3118        assert_eq!(node.is_delete(), expected);
3119    }
3120
3121    #[rstest]
3122    #[case(Node::Link(Link{url: Url::new("test".to_string()), values: Vec::new(), title: None, position: None}), true)]
3123    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3124    fn test_is_link(#[case] node: Node, #[case] expected: bool) {
3125        assert_eq!(node.is_link(), expected);
3126    }
3127
3128    #[rstest]
3129    #[case(Node::LinkRef(LinkRef{ident: "test".to_string(), values: Vec::new(), label: None, position: None}), true)]
3130    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3131    fn test_is_link_ref(#[case] node: Node, #[case] expected: bool) {
3132        assert_eq!(node.is_link_ref(), expected);
3133    }
3134
3135    #[rstest]
3136    #[case(Node::Image(Image{alt: "alt".to_string(), url: "test".to_string(), title: None, position: None}), true)]
3137    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3138    fn test_is_image(#[case] node: Node, #[case] expected: bool) {
3139        assert_eq!(node.is_image(), expected);
3140    }
3141
3142    #[rstest]
3143    #[case(Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "test".to_string(), label: None, position: None}), true)]
3144    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3145    fn test_is_image_ref(#[case] node: Node, #[case] expected: bool) {
3146        assert_eq!(node.is_image_ref(), expected);
3147    }
3148
3149    #[rstest]
3150    #[case(Node::Code(Code{value: "code".to_string(), lang: Some("rust".to_string()), fence: true, meta: None, position: None}), true, Some("rust".into()))]
3151    #[case(Node::Code(Code{value: "code".to_string(), lang: Some("rust".to_string()), fence: true, meta: None, position: None}), false, Some("python".into()))]
3152    #[case(Node::Code(Code{value: "code".to_string(), lang: None, fence: true, meta: None, position: None}), true, None)]
3153    #[case(Node::Code(Code{value: "code".to_string(), lang: None, fence: false, meta: None, position: None}), true, None)]
3154    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false, None)]
3155    fn test_is_code(#[case] node: Node, #[case] expected: bool, #[case] lang: Option<SmolStr>) {
3156        assert_eq!(node.is_code(lang), expected);
3157    }
3158
3159    #[rstest]
3160    #[case(Node::Heading(Heading{depth: 1, values: vec!["test".to_string().into()], position: None}), true, Some(1))]
3161    #[case(Node::Heading(Heading{depth: 2, values: vec!["test".to_string().into()], position: None}), false, Some(1))]
3162    #[case(Node::Heading(Heading{depth: 1, values: vec!["test".to_string().into()], position: None}), true, None)]
3163    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false, None)]
3164    fn test_is_heading(#[case] node: Node, #[case] expected: bool, #[case] depth: Option<u8>) {
3165        assert_eq!(node.is_heading(depth), expected);
3166    }
3167
3168    #[rstest]
3169    #[case(Node::HorizontalRule(HorizontalRule{position: None}), true)]
3170    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3171    fn test_is_horizontal_rule(#[case] node: Node, #[case] expected: bool) {
3172        assert_eq!(node.is_horizontal_rule(), expected);
3173    }
3174
3175    #[rstest]
3176    #[case(Node::Blockquote(Blockquote{values: vec!["test".to_string().into()], position: None}), true)]
3177    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3178    fn test_is_blockquote(#[case] node: Node, #[case] expected: bool) {
3179        assert_eq!(node.is_blockquote(), expected);
3180    }
3181
3182    #[rstest]
3183    #[case(Node::Html(Html{value: "<div>test</div>".to_string(), position: None}), true)]
3184    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3185    fn test_is_html(#[case] node: Node, #[case] expected: bool) {
3186        assert_eq!(node.is_html(), expected);
3187    }
3188
3189    #[rstest]
3190    #[case(Node::node_values(
3191           &Node::Strong(Strong{values: vec!["test".to_string().into()], position: None})),
3192           vec!["test".to_string().into()])]
3193    #[case(Node::node_values(
3194           &Node::Text(Text{value: "test".to_string(), position: None})),
3195           vec!["test".to_string().into()])]
3196    #[case(Node::node_values(
3197           &Node::Blockquote(Blockquote{values: vec!["test".to_string().into()], position: None})),
3198           vec!["test".to_string().into()])]
3199    #[case(Node::node_values(
3200           &Node::Delete(Delete{values: vec!["test".to_string().into()], position: None})),
3201           vec!["test".to_string().into()])]
3202    #[case(Node::node_values(
3203           &Node::Emphasis(Emphasis{values: vec!["test".to_string().into()], position: None})),
3204           vec!["test".to_string().into()])]
3205    #[case(Node::node_values(
3206           &Node::Heading(Heading{depth: 1, values: vec!["test".to_string().into()], position: None})),
3207           vec!["test".to_string().into()])]
3208    #[case(Node::node_values(
3209           &Node::List(List{values: vec!["test".to_string().into()], ordered: false, level: 1, checked: Some(false), index: 0, position: None})),
3210           vec!["test".to_string().into()])]
3211    fn test_node_value(#[case] actual: Vec<Node>, #[case] expected: Vec<Node>) {
3212        assert_eq!(actual, expected);
3213    }
3214
3215    #[rstest]
3216    #[case(Node::Footnote(Footnote{ident: "test".to_string(), values: Vec::new(), position: None}), true)]
3217    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3218    fn test_is_footnote(#[case] node: Node, #[case] expected: bool) {
3219        assert_eq!(node.is_footnote(), expected);
3220    }
3221
3222    #[rstest]
3223    #[case(Node::FootnoteRef(FootnoteRef{ident: "test".to_string(), label: None, position: None}), true)]
3224    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3225    fn test_is_footnote_ref(#[case] node: Node, #[case] expected: bool) {
3226        assert_eq!(node.is_footnote_ref(), expected);
3227    }
3228
3229    #[rstest]
3230    #[case(Node::Math(Math{value: "x^2".to_string(), position: None}), true)]
3231    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3232    fn test_is_math(#[case] node: Node, #[case] expected: bool) {
3233        assert_eq!(node.is_math(), expected);
3234    }
3235
3236    #[rstest]
3237    #[case(Node::Break(Break{position: None}), true)]
3238    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3239    fn test_is_break(#[case] node: Node, #[case] expected: bool) {
3240        assert_eq!(node.is_break(), expected);
3241    }
3242
3243    #[rstest]
3244    #[case(Node::Yaml(Yaml{value: "key: value".to_string(), position: None}), true)]
3245    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3246    fn test_is_yaml(#[case] node: Node, #[case] expected: bool) {
3247        assert_eq!(node.is_yaml(), expected);
3248    }
3249
3250    #[rstest]
3251    #[case(Node::Toml(Toml{value: "key = \"value\"".to_string(), position: None}), true)]
3252    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3253    fn test_is_toml(#[case] node: Node, #[case] expected: bool) {
3254        assert_eq!(node.is_toml(), expected);
3255    }
3256
3257    #[rstest]
3258    #[case(Node::Definition(Definition{ident: "ident".to_string(), url: Url::new("url".to_string()), title: None, label: None, position: None}), true)]
3259    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3260    fn test_is_definition(#[case] node: Node, #[case] expected: bool) {
3261        assert_eq!(node.is_definition(), expected);
3262    }
3263
3264    #[rstest]
3265    #[case(Node::Emphasis(Emphasis{values: vec!["test".to_string().into()], position: None}), true)]
3266    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3267    fn test_is_emphasis(#[case] node: Node, #[case] expected: bool) {
3268        assert_eq!(node.is_emphasis(), expected);
3269    }
3270
3271    #[rstest]
3272    #[case(Node::MdxFlowExpression(MdxFlowExpression{value: "test".into(), position: None}), true)]
3273    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3274    fn test_is_mdx_flow_expression(#[case] node: Node, #[case] expected: bool) {
3275        assert_eq!(node.is_mdx_flow_expression(), expected);
3276    }
3277
3278    #[rstest]
3279    #[case(Node::MdxTextExpression(MdxTextExpression{value: "test".into(), position: None}), true)]
3280    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3281    fn test_is_mdx_text_expression(#[case] node: Node, #[case] expected: bool) {
3282        assert_eq!(node.is_mdx_text_expression(), expected);
3283    }
3284
3285    #[rstest]
3286    #[case(Node::MdxJsxFlowElement(MdxJsxFlowElement{name: None, attributes: Vec::new(), children: Vec::new(), position: None}), true)]
3287    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3288    fn test_is_mdx_jsx_flow_element(#[case] node: Node, #[case] expected: bool) {
3289        assert_eq!(node.is_mdx_jsx_flow_element(), expected);
3290    }
3291
3292    #[rstest]
3293    #[case(Node::MdxJsxTextElement(MdxJsxTextElement{name: None, attributes: Vec::new(), children: Vec::new(), position: None}), true)]
3294    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3295    fn test_is_mdx_jsx_text_element(#[case] node: Node, #[case] expected: bool) {
3296        assert_eq!(node.is_mdx_jsx_text_element(), expected);
3297    }
3298
3299    #[rstest]
3300    #[case(Node::MdxJsEsm(MdxJsEsm{value: "test".into(), position: None}), true)]
3301    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
3302    fn test_is_msx_js_esm(#[case] node: Node, #[case] expected: bool) {
3303        assert_eq!(node.is_mdx_js_esm(), expected);
3304    }
3305
3306    #[rstest]
3307    #[case::text(Node::Text(Text{value: "test".to_string(), position: None }), RenderOptions::default(), "test")]
3308    #[case::list(Node::List(List{index: 0, level: 2, checked: None, ordered: false, values: vec!["test".to_string().into()], position: None}), RenderOptions::default(), "    - test")]
3309    #[case::list(Node::List(List{index: 0, level: 1, checked: None, ordered: false, values: vec!["test".to_string().into()], position: None}), RenderOptions { list_style: ListStyle::Plus, ..Default::default() }, "  + test")]
3310    #[case::list(Node::List(List{index: 0, level: 1, checked: Some(true), ordered: false, values: vec!["test".to_string().into()], position: None}), RenderOptions { list_style: ListStyle::Star, ..Default::default() }, "  * [x] test")]
3311    #[case::list(Node::List(List{index: 0, level: 1, checked: Some(false), ordered: false, values: vec!["test".to_string().into()], position: None}), RenderOptions::default(), "  - [ ] test")]
3312    #[case::list(Node::List(List{index: 0, level: 1, checked: None, ordered: true, values: vec!["test".to_string().into()], position: None}), RenderOptions::default(), "  1. test")]
3313    #[case::list(Node::List(List{index: 0, level: 1, checked: Some(false), ordered: true, values: vec!["test".to_string().into()], position: None}), RenderOptions::default(), "  1. [ ] test")]
3314    #[case::table_row(Node::TableRow(TableRow{values: vec![Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec!["test".to_string().into()], position: None})], position: None}), RenderOptions::default(), "|test")]
3315    #[case::table_row(Node::TableRow(TableRow{values: vec![Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: true, last_cell_of_in_table: false, values: vec!["test".to_string().into()], position: None})], position: None}), RenderOptions::default(), "|test|")]
3316    #[case::table_cell(Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec!["test".to_string().into()], position: None}), RenderOptions::default(), "|test")]
3317    #[case::table_cell(Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: true, last_cell_of_in_table: false, values: vec!["test".to_string().into()], position: None}), RenderOptions::default(), "|test|")]
3318    #[case::table_header(Node::TableHeader(TableHeader{align: vec![TableAlignKind::Left, TableAlignKind::Right, TableAlignKind::Center, TableAlignKind::None], position: None}), RenderOptions::default(), "|:---|---:|:---:|---|")]
3319    #[case::block_quote(Node::Blockquote(Blockquote{values: vec!["test".to_string().into()], position: None}), RenderOptions::default(), "> test")]
3320    #[case::block_quote(Node::Blockquote(Blockquote{values: vec!["test\ntest2".to_string().into()], position: None}), RenderOptions::default(), "> test\n> test2")]
3321    #[case::code(Node::Code(Code{value: "code".to_string(), lang: Some("rust".to_string()), fence: true, meta: None, position: None}), RenderOptions::default(), "```rust\ncode\n```")]
3322    #[case::code(Node::Code(Code{value: "code".to_string(), lang: None, fence: true, meta: None, position: None}), RenderOptions::default(), "```\ncode\n```")]
3323    #[case::code(Node::Code(Code{value: "code".to_string(), lang: None, fence: false, meta: None, position: None}), RenderOptions::default(), "    code")]
3324    #[case::code(Node::Code(Code{value: "code".to_string(), lang: Some("rust".to_string()), fence: true, meta: Some("meta".to_string()), position: None}), RenderOptions::default(), "```rust meta\ncode\n```")]
3325    #[case::definition(Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: None, label: Some("label".to_string()), position: None}), RenderOptions::default(), "[label]: url")]
3326    #[case::definition(Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: Some(Title::new("title".to_string())), label: Some("label".to_string()), position: None}), RenderOptions::default(), "[label]: url \"title\"")]
3327    #[case::definition(Node::Definition(Definition{ident: "id".to_string(), url: Url::new("".to_string()), title: None, label: Some("label".to_string()), position: None}), RenderOptions::default(), "[label]: ")]
3328    #[case::delete(Node::Delete(Delete{values: vec!["test".to_string().into()], position: None}), RenderOptions::default(), "~~test~~")]
3329    #[case::emphasis(Node::Emphasis(Emphasis{values: vec!["test".to_string().into()], position: None}), RenderOptions::default(), "*test*")]
3330    #[case::footnote(Node::Footnote(Footnote{ident: "id".to_string(), values: vec!["label".to_string().into()], position: None}), RenderOptions::default(), "[^id]: label")]
3331    #[case::footnote_ref(Node::FootnoteRef(FootnoteRef{ident: "label".to_string(), label: Some("label".to_string()), position: None}), RenderOptions::default(), "[^label]")]
3332    #[case::heading(Node::Heading(Heading{depth: 1, values: vec!["test".to_string().into()], position: None}), RenderOptions::default(), "# test")]
3333    #[case::heading(Node::Heading(Heading{depth: 3, values: vec!["test".to_string().into()], position: None}), RenderOptions::default(), "### test")]
3334    #[case::html(Node::Html(Html{value: "<div>test</div>".to_string(), position: None}), RenderOptions::default(), "<div>test</div>")]
3335    #[case::image(Node::Image(Image{alt: "alt".to_string(), url: "url".to_string(), title: None, position: None}), RenderOptions::default(), "![alt](url)")]
3336    #[case::image(Node::Image(Image{alt: "alt".to_string(), url: "url with space".to_string(), title: Some("title".to_string()), position: None}), RenderOptions::default(), "![alt](url%20with%20space \"title\")")]
3337    #[case::image_ref(Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "id".to_string(), label: Some("id".to_string()), position: None}), RenderOptions::default(), "![alt][id]")]
3338    #[case::image_ref(Node::ImageRef(ImageRef{alt: "id".to_string(), ident: "id".to_string(), label: Some("id".to_string()), position: None}), RenderOptions::default(), "![id]")]
3339    #[case::code_inline(Node::CodeInline(CodeInline{value: "code".into(), position: None}), RenderOptions::default(), "`code`")]
3340    #[case::math_inline(Node::MathInline(MathInline{value: "x^2".into(), position: None}), RenderOptions::default(), "$x^2$")]
3341    #[case::link(Node::Link(Link{url: Url::new("url".to_string()), title: Some(Title::new("title".to_string())), values: vec!["value".to_string().into()], position: None}), RenderOptions::default(), "[value](url \"title\")")]
3342    #[case::link(Node::Link(Link{url: Url::new("".to_string()), title: None, values: vec!["value".to_string().into()], position: None}), RenderOptions::default(), "[value]()")]
3343    #[case::link(Node::Link(Link{url: Url::new("url".to_string()), title: None, values: vec!["value".to_string().into()], position: None}), RenderOptions::default(), "[value](url)")]
3344    #[case::link_ref(Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec!["id".to_string().into()], label: Some("id".to_string()), position: None}), RenderOptions::default(), "[id]")]
3345    #[case::link_ref(Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec!["open".to_string().into()], label: Some("id".to_string()), position: None}), RenderOptions::default(), "[open][id]")]
3346    #[case::math(Node::Math(Math{value: "x^2".to_string(), position: None}), RenderOptions::default(), "$$\nx^2\n$$")]
3347    #[case::strong(Node::Strong(Strong{values: vec!["test".to_string().into()], position: None}), RenderOptions::default(), "**test**")]
3348    #[case::yaml(Node::Yaml(Yaml{value: "key: value".to_string(), position: None}), RenderOptions::default(), "---\nkey: value\n---")]
3349    #[case::toml(Node::Toml(Toml{value: "key = \"value\"".to_string(), position: None}), RenderOptions::default(), "+++\nkey = \"value\"\n+++")]
3350    #[case::break_(Node::Break(Break{position: None}), RenderOptions::default(), "\\")]
3351    #[case::horizontal_rule(Node::HorizontalRule(HorizontalRule{position: None}), RenderOptions::default(), "---")]
3352    #[case::mdx_jsx_flow_element(Node::MdxJsxFlowElement(MdxJsxFlowElement{
3353        name: Some("div".to_string()),
3354        attributes: vec![
3355            MdxAttributeContent::Property(MdxJsxAttribute {
3356                name: "className".into(),
3357                value: Some(MdxAttributeValue::Literal("container".into()))
3358            })
3359        ],
3360        children: vec![
3361            "content".to_string().into()
3362        ],
3363        position: None
3364    }), RenderOptions::default(), "<div className=\"container\">content</div>")]
3365    #[case::mdx_jsx_flow_element(Node::MdxJsxFlowElement(MdxJsxFlowElement{
3366        name: Some("div".to_string()),
3367        attributes: vec![
3368            MdxAttributeContent::Property(MdxJsxAttribute {
3369                name: "className".into(),
3370                value: Some(MdxAttributeValue::Literal("container".into()))
3371            })
3372        ],
3373        children: Vec::new(),
3374        position: None
3375    }), RenderOptions::default(), "<div className=\"container\" />")]
3376    #[case::mdx_jsx_flow_element(Node::MdxJsxFlowElement(MdxJsxFlowElement{
3377        name: Some("div".to_string()),
3378        attributes: Vec::new(),
3379        children: Vec::new(),
3380        position: None
3381    }), RenderOptions::default(), "<div />")]
3382    #[case::mdx_jsx_text_element(Node::MdxJsxTextElement(MdxJsxTextElement{
3383        name: Some("span".into()),
3384        attributes: vec![
3385            MdxAttributeContent::Expression("...props".into())
3386        ],
3387        children: vec![
3388            "inline".to_string().into()
3389        ],
3390        position: None
3391    }), RenderOptions::default(), "<span {...props}>inline</span>")]
3392    #[case::mdx_jsx_text_element(Node::MdxJsxTextElement(MdxJsxTextElement{
3393        name: Some("span".into()),
3394        attributes: vec![
3395            MdxAttributeContent::Expression("...props".into())
3396        ],
3397        children: vec![
3398        ],
3399        position: None
3400    }), RenderOptions::default(), "<span {...props} />")]
3401    #[case::mdx_jsx_text_element(Node::MdxJsxTextElement(MdxJsxTextElement{
3402        name: Some("span".into()),
3403        attributes: vec![
3404        ],
3405        children: vec![
3406        ],
3407        position: None
3408    }), RenderOptions::default(), "<span />")]
3409    #[case(Node::MdxTextExpression(MdxTextExpression{
3410        value: "count + 1".into(),
3411        position: None,
3412    }), RenderOptions::default(), "{count + 1}")]
3413    #[case(Node::MdxJsEsm(MdxJsEsm{
3414        value: "import React from 'react'".into(),
3415        position: None,
3416    }), RenderOptions::default(), "import React from 'react'")]
3417    fn test_to_string_with(#[case] node: Node, #[case] options: RenderOptions, #[case] expected: &str) {
3418        assert_eq!(node.to_string_with(&options), expected);
3419    }
3420
3421    #[test]
3422    fn test_node_partial_ord() {
3423        let node1 = Node::Text(Text {
3424            value: "test1".to_string(),
3425            position: Some(Position {
3426                start: Point { line: 1, column: 1 },
3427                end: Point { line: 1, column: 5 },
3428            }),
3429        });
3430
3431        let node2 = Node::Text(Text {
3432            value: "test2".to_string(),
3433            position: Some(Position {
3434                start: Point { line: 1, column: 6 },
3435                end: Point { line: 1, column: 10 },
3436            }),
3437        });
3438
3439        let node3 = Node::Text(Text {
3440            value: "test3".to_string(),
3441            position: Some(Position {
3442                start: Point { line: 2, column: 1 },
3443                end: Point { line: 2, column: 5 },
3444            }),
3445        });
3446
3447        assert_eq!(node1.partial_cmp(&node2), Some(std::cmp::Ordering::Less));
3448        assert_eq!(node2.partial_cmp(&node1), Some(std::cmp::Ordering::Greater));
3449
3450        assert_eq!(node1.partial_cmp(&node3), Some(std::cmp::Ordering::Less));
3451        assert_eq!(node3.partial_cmp(&node1), Some(std::cmp::Ordering::Greater));
3452
3453        let node4 = Node::Text(Text {
3454            value: "test4".to_string(),
3455            position: None,
3456        });
3457
3458        assert_eq!(node1.partial_cmp(&node4), Some(std::cmp::Ordering::Less));
3459        assert_eq!(node4.partial_cmp(&node1), Some(std::cmp::Ordering::Greater));
3460
3461        let node5 = Node::Text(Text {
3462            value: "test5".to_string(),
3463            position: None,
3464        });
3465
3466        assert_eq!(node4.partial_cmp(&node5), Some(std::cmp::Ordering::Equal));
3467
3468        let node6 = Node::Code(Code {
3469            value: "code".to_string(),
3470            lang: None,
3471            fence: true,
3472            meta: None,
3473            position: None,
3474        });
3475
3476        assert_eq!(node6.partial_cmp(&node4), Some(std::cmp::Ordering::Less));
3477        assert_eq!(node4.partial_cmp(&node6), Some(std::cmp::Ordering::Greater));
3478    }
3479
3480    #[rstest]
3481    #[case(Node::Blockquote(Blockquote{values: Vec::new(), position: None}), "blockquote")]
3482    #[case(Node::Break(Break{position: None}), "break")]
3483    #[case(Node::Definition(Definition{ident: "".to_string(), url: Url::new("".to_string()), title: None, label: None, position: None}), "definition")]
3484    #[case(Node::Delete(Delete{values: Vec::new(), position: None}), "delete")]
3485    #[case(Node::Heading(Heading{depth: 1, values: Vec::new(), position: None}), "h1")]
3486    #[case(Node::Heading(Heading{depth: 2, values: Vec::new(), position: None}), "h2")]
3487    #[case(Node::Heading(Heading{depth: 3, values: Vec::new(), position: None}), "h3")]
3488    #[case(Node::Heading(Heading{depth: 4, values: Vec::new(), position: None}), "h4")]
3489    #[case(Node::Heading(Heading{depth: 5, values: Vec::new(), position: None}), "h5")]
3490    #[case(Node::Heading(Heading{depth: 6, values: Vec::new(), position: None}), "h6")]
3491    #[case(Node::Heading(Heading{depth: 7, values: Vec::new(), position: None}), "h")]
3492    #[case(Node::Emphasis(Emphasis{values: Vec::new(), position: None}), "emphasis")]
3493    #[case(Node::Footnote(Footnote{ident: "".to_string(), values: Vec::new(), position: None}), "footnote")]
3494    #[case(Node::FootnoteRef(FootnoteRef{ident: "".to_string(), label: None, position: None}), "footnoteref")]
3495    #[case(Node::Html(Html{value: "".to_string(), position: None}), "html")]
3496    #[case(Node::Yaml(Yaml{value: "".to_string(), position: None}), "yaml")]
3497    #[case(Node::Toml(Toml{value: "".to_string(), position: None}), "toml")]
3498    #[case(Node::Image(Image{alt: "".to_string(), url: "".to_string(), title: None, position: None}), "image")]
3499    #[case(Node::ImageRef(ImageRef{alt: "".to_string(), ident: "".to_string(), label: None, position: None}), "image_ref")]
3500    #[case(Node::CodeInline(CodeInline{value: "".into(), position: None}), "code_inline")]
3501    #[case(Node::MathInline(MathInline{value: "".into(), position: None}), "math_inline")]
3502    #[case(Node::Link(Link{url: Url::new("".to_string()), title: None, values: Vec::new(), position: None}), "link")]
3503    #[case(Node::LinkRef(LinkRef{ident: "".to_string(), values: Vec::new(), label: None, position: None}), "link_ref")]
3504    #[case(Node::Math(Math{value: "".to_string(), position: None}), "math")]
3505    #[case(Node::List(List{index: 0, level: 0, checked: None, ordered: false, values: Vec::new(), position: None}), "list")]
3506    #[case(Node::TableHeader(TableHeader{align: Vec::new(), position: None}), "table_header")]
3507    #[case(Node::TableRow(TableRow{values: Vec::new(), position: None}), "table_row")]
3508    #[case(Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: Vec::new(), position: None}), "table_cell")]
3509    #[case(Node::Code(Code{value: "".to_string(), lang: None, fence: true, meta: None, position: None}), "code")]
3510    #[case(Node::Strong(Strong{values: Vec::new(), position: None}), "strong")]
3511    #[case(Node::HorizontalRule(HorizontalRule{position: None}), "Horizontal_rule")]
3512    #[case(Node::MdxFlowExpression(MdxFlowExpression{value: "".into(), position: None}), "mdx_flow_expression")]
3513    #[case(Node::MdxJsxFlowElement(MdxJsxFlowElement{name: None, attributes: Vec::new(), children: Vec::new(), position: None}), "mdx_jsx_flow_element")]
3514    #[case(Node::MdxJsxTextElement(MdxJsxTextElement{name: None, attributes: Vec::new(), children: Vec::new(), position: None}), "mdx_jsx_text_element")]
3515    #[case(Node::MdxTextExpression(MdxTextExpression{value: "".into(), position: None}), "mdx_text_expression")]
3516    #[case(Node::MdxJsEsm(MdxJsEsm{value: "".into(), position: None}), "mdx_js_esm")]
3517    #[case(Node::Text(Text{value: "".to_string(), position: None}), "text")]
3518    fn test_name(#[case] node: Node, #[case] expected: &str) {
3519        assert_eq!(node.name(), expected);
3520    }
3521
3522    #[rstest]
3523    #[case(Node::Text(Text{value: "test".to_string(), position: None}), "test")]
3524    #[case(Node::List(List{index: 0, level: 0, checked: None, ordered: false, values: vec![Node::Text(Text{value: "test".to_string(), position: None})], position: None}), "test")]
3525    #[case(Node::Blockquote(Blockquote{values: vec![Node::Text(Text{value: "test".to_string(), position: None})], position: None}), "test")]
3526    #[case(Node::Delete(Delete{values: vec![Node::Text(Text{value: "test".to_string(), position: None})], position: None}), "test")]
3527    #[case(Node::Heading(Heading{depth: 1, values: vec![Node::Text(Text{value: "test".to_string(), position: None})], position: None}), "test")]
3528    #[case(Node::Emphasis(Emphasis{values: vec![Node::Text(Text{value: "test".to_string(), position: None})], position: None}), "test")]
3529    #[case(Node::Footnote(Footnote{ident: "test".to_string(), values: vec![Node::Text(Text{value: "test".to_string(), position: None})], position: None}), "test")]
3530    #[case(Node::FootnoteRef(FootnoteRef{ident: "test".to_string(), label: None, position: None}), "test")]
3531    #[case(Node::Html(Html{value: "test".to_string(), position: None}), "test")]
3532    #[case(Node::Yaml(Yaml{value: "test".to_string(), position: None}), "test")]
3533    #[case(Node::Toml(Toml{value: "test".to_string(), position: None}), "test")]
3534    #[case(Node::Image(Image{alt: "alt".to_string(), url: "test".to_string(), title: None, position: None}), "test")]
3535    #[case(Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "test".to_string(), label: None, position: None}), "test")]
3536    #[case(Node::CodeInline(CodeInline{value: "test".into(), position: None}), "test")]
3537    #[case(Node::MathInline(MathInline{value: "test".into(), position: None}), "test")]
3538    #[case(Node::Link(Link{url: Url::new("test".to_string()), title: None, values: Vec::new(), position: None}), "test")]
3539    #[case(Node::LinkRef(LinkRef{ident: "test".to_string(), values: Vec::new(), label: None, position: None}), "test")]
3540    #[case(Node::Math(Math{value: "test".to_string(), position: None}), "test")]
3541    #[case(Node::Code(Code{value: "test".to_string(), lang: None, fence: true, meta: None, position: None}), "test")]
3542    #[case(Node::Strong(Strong{values: vec![Node::Text(Text{value: "test".to_string(), position: None})], position: None}), "test")]
3543    #[case(Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![Node::Text(Text{value: "test".to_string(), position: None})], position: None}), "test")]
3544    #[case(Node::TableRow(TableRow{values: vec![Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![Node::Text(Text{value: "test".to_string(), position: None})], position: None})], position: None}), "test")]
3545    #[case(Node::Break(Break{position: None}), "")]
3546    #[case(Node::HorizontalRule(HorizontalRule{position: None}), "")]
3547    #[case(Node::TableHeader(TableHeader{align: Vec::new(), position: None}), "")]
3548    #[case(Node::MdxFlowExpression(MdxFlowExpression{value: "test".into(), position: None}), "test")]
3549    #[case(Node::MdxTextExpression(MdxTextExpression{value: "test".into(), position: None}), "test")]
3550    #[case(Node::MdxJsEsm(MdxJsEsm{value: "test".into(), position: None}), "test")]
3551    #[case(Node::MdxJsxFlowElement(MdxJsxFlowElement{name: Some("name".to_string()), attributes: Vec::new(), children: vec![Node::Text(Text{value: "test".to_string(), position: None})],  position: None}), "test")]
3552    #[case(Node::Definition(Definition{ident: "test".to_string(), url: Url::new("url".to_string()), title: None, label: None, position: None}), "url")]
3553    #[case(Node::Fragment(Fragment {values: vec![Node::Text(Text{value: "test".to_string(), position: None})]}), "test")]
3554    fn test_value(#[case] node: Node, #[case] expected: &str) {
3555        assert_eq!(node.value(), expected);
3556    }
3557
3558    #[rstest]
3559    #[case(Node::Text(Text{value: "test".to_string(), position: None}), None)]
3560    #[case(Node::Text(Text{value: "test".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3561    #[case(Node::List(List{index: 0, level: 0, checked: None, ordered: false, values: Vec::new(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3562    #[case(Node::Blockquote(Blockquote{values: Vec::new(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3563    #[case(Node::Delete(Delete{values: Vec::new(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3564    #[case(Node::Heading(Heading{depth: 1, values: Vec::new(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3565    #[case(Node::Emphasis(Emphasis{values: Vec::new(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3566    #[case(Node::Footnote(Footnote{ident: "".to_string(), values: Vec::new(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3567    #[case(Node::FootnoteRef(FootnoteRef{ident: "".to_string(), label: None, position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3568    #[case(Node::Html(Html{value: "".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3569    #[case(Node::Yaml(Yaml{value: "".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3570    #[case(Node::Toml(Toml{value: "".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3571    #[case(Node::Image(Image{alt: "".to_string(), url: "".to_string(), title: None, position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3572    #[case(Node::ImageRef(ImageRef{alt: "".to_string(), ident: "".to_string(), label: None, position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3573    #[case(Node::CodeInline(CodeInline{value: "".into(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3574    #[case(Node::MathInline(MathInline{value: "".into(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3575    #[case(Node::Link(Link{url: Url("".to_string()), title: None, values: Vec::new(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3576    #[case(Node::LinkRef(LinkRef{ident: "".to_string(), values: Vec::new(), label: None, position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3577    #[case(Node::Math(Math{value: "".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3578    #[case(Node::Code(Code{value: "".to_string(), lang: None, fence: true, meta: None, position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3579    #[case(Node::Strong(Strong{values: Vec::new(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3580    #[case(Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: Vec::new(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3581    #[case(Node::TableRow(TableRow{values: Vec::new(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3582    #[case(Node::TableHeader(TableHeader{align: Vec::new(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3583    #[case(Node::Break(Break{position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3584    #[case(Node::HorizontalRule(HorizontalRule{position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3585    #[case(Node::MdxFlowExpression(MdxFlowExpression{value: "test".into(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3586    #[case(Node::MdxTextExpression(MdxTextExpression{value: "test".into(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3587    #[case(Node::MdxJsEsm(MdxJsEsm{value: "test".into(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3588    #[case(Node::MdxJsxFlowElement(MdxJsxFlowElement{name: Some("div".to_string()), attributes: Vec::new(), children: Vec::new(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3589    #[case(Node::MdxJsxTextElement(MdxJsxTextElement{name: Some("span".into()), attributes: Vec::new(), children: Vec::new(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3590    #[case(Node::Definition(Definition{ident: "".to_string(), url: Url("".to_string()), title: None, label: None, position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3591    #[case(Node::Fragment(Fragment{values: Vec::new()}), None)]
3592    #[case(Node::Fragment(Fragment{values: vec![
3593        Node::Text(Text{value: "test1".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}),
3594        Node::Text(Text{value: "test2".to_string(), position: Some(Position{start: Point{line: 1, column: 6}, end: Point{line: 1, column: 10}})})
3595    ]}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}}))]
3596    #[case(Node::Fragment(Fragment{values: vec![
3597        Node::Text(Text{value: "test".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}),
3598        Node::Text(Text{value: "test2".to_string(), position: None})
3599    ]}), Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}}))]
3600    #[case(Node::Fragment(Fragment{values: vec![
3601        Node::Text(Text{value: "test".to_string(), position: None}),
3602        Node::Text(Text{value: "test2".to_string(), position: Some(Position{start: Point{line: 1, column: 6}, end: Point{line: 1, column: 10}})})
3603    ]}), Some(Position{start: Point{line: 1, column: 6}, end: Point{line: 1, column: 10}}))]
3604    #[case(Node::Fragment(Fragment{values: vec![
3605        Node::Text(Text{value: "test2".to_string(), position: Some(Position{start: Point{line: 1, column: 6}, end: Point{line: 1, column: 10}})}),
3606        Node::Text(Text{value: "test".to_string(), position: None})
3607    ]}), Some(Position{start: Point{line: 1, column: 6}, end: Point{line: 1, column: 10}}))]
3608    #[case(Node::Fragment(Fragment{values: vec![
3609        Node::Text(Text{value: "test".to_string(), position: None}),
3610        Node::Text(Text{value: "test2".to_string(), position: None})
3611    ]}), None)]
3612    #[case(Node::Empty, None)]
3613    fn test_position(#[case] node: Node, #[case] expected: Option<Position>) {
3614        assert_eq!(node.position(), expected);
3615    }
3616
3617    #[rstest]
3618    #[case(Node::Blockquote(Blockquote{values: vec![
3619        Node::Text(Text{value: "first".to_string(), position: None}),
3620        Node::Text(Text{value: "second".to_string(), position: None})
3621    ], position: None}), 0, Some(Node::Text(Text{value: "first".to_string(), position: None})))]
3622    #[case(Node::Blockquote(Blockquote{values: vec![
3623        Node::Text(Text{value: "first".to_string(), position: None}),
3624        Node::Text(Text{value: "second".to_string(), position: None})
3625    ], position: None}), 1, Some(Node::Text(Text{value: "second".to_string(), position: None})))]
3626    #[case(Node::Blockquote(Blockquote{values: vec![
3627        Node::Text(Text{value: "first".to_string(), position: None})
3628    ], position: None}), 1, None)]
3629    #[case(Node::Delete(Delete{values: vec![
3630        Node::Text(Text{value: "first".to_string(), position: None}),
3631        Node::Text(Text{value: "second".to_string(), position: None})
3632    ], position: None}), 0, Some(Node::Text(Text{value: "first".to_string(), position: None})))]
3633    #[case(Node::Emphasis(Emphasis{values: vec![
3634        Node::Text(Text{value: "first".to_string(), position: None}),
3635        Node::Text(Text{value: "second".to_string(), position: None})
3636    ], position: None}), 1, Some(Node::Text(Text{value: "second".to_string(), position: None})))]
3637    #[case(Node::Strong(Strong{values: vec![
3638        Node::Text(Text{value: "first".to_string(), position: None})
3639    ], position: None}), 0, Some(Node::Text(Text{value: "first".to_string(), position: None})))]
3640    #[case(Node::Heading(Heading{depth: 1, values: vec![
3641        Node::Text(Text{value: "first".to_string(), position: None}),
3642        Node::Text(Text{value: "second".to_string(), position: None})
3643    ], position: None}), 0, Some(Node::Text(Text{value: "first".to_string(), position: None})))]
3644    #[case(Node::List(List{index: 0, level: 0, checked: None, ordered: false, values: vec![
3645        Node::Text(Text{value: "first".to_string(), position: None}),
3646        Node::Text(Text{value: "second".to_string(), position: None})
3647    ], position: None}), 1, Some(Node::Text(Text{value: "second".to_string(), position: None})))]
3648    #[case(Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![
3649        Node::Text(Text{value: "cell content".to_string(), position: None})
3650    ], position: None}), 0, Some(Node::Text(Text{value: "cell content".to_string(), position: None})))]
3651    #[case(Node::TableRow(TableRow{values: vec![
3652        Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: Vec::new(), position: None}),
3653        Node::TableCell(TableCell{column: 1, row: 0, last_cell_in_row: true, last_cell_of_in_table: false, values: Vec::new(), position: None})
3654    ], position: None}), 1, Some(Node::TableCell(TableCell{column: 1, row: 0, last_cell_in_row: true, last_cell_of_in_table: false, values: Vec::new(), position: None})))]
3655    #[case(Node::Text(Text{value: "plain text".to_string(), position: None}), 0, None)]
3656    #[case(Node::Code(Code{value: "code".to_string(), lang: None, fence: true, meta: None, position: None}), 0, None)]
3657    #[case(Node::Html(Html{value: "<div>".to_string(), position: None}), 0, None)]
3658    fn test_find_at_index(#[case] node: Node, #[case] index: usize, #[case] expected: Option<Node>) {
3659        assert_eq!(node.find_at_index(index), expected);
3660    }
3661
3662    #[rstest]
3663    #[case(Node::Blockquote(Blockquote{values: vec!["test".to_string().into()], position: None}),
3664           Node::Fragment(Fragment{values: vec!["test".to_string().into()]}))]
3665    #[case(Node::Delete(Delete{values: vec!["test".to_string().into()], position: None}),
3666           Node::Fragment(Fragment{values: vec!["test".to_string().into()]}))]
3667    #[case(Node::Heading(Heading{depth: 1, values: vec!["test".to_string().into()], position: None}),
3668           Node::Fragment(Fragment{values: vec!["test".to_string().into()]}))]
3669    #[case(Node::Emphasis(Emphasis{values: vec!["test".to_string().into()], position: None}),
3670           Node::Fragment(Fragment{values: vec!["test".to_string().into()]}))]
3671    #[case(Node::List(List{index: 0, level: 0, checked: None, ordered: false, values: vec!["test".to_string().into()], position: None}),
3672           Node::Fragment(Fragment{values: vec!["test".to_string().into()]}))]
3673    #[case(Node::Strong(Strong{values: vec!["test".to_string().into()], position: None}),
3674           Node::Fragment(Fragment{values: vec!["test".to_string().into()]}))]
3675    #[case(Node::Link(Link{url: Url("url".to_string()), title: None, values: vec!["test".to_string().into()], position: None}),
3676           Node::Fragment(Fragment{values: vec!["test".to_string().into()]}))]
3677    #[case(Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec!["test".to_string().into()], label: None, position: None}),
3678           Node::Fragment(Fragment{values: vec!["test".to_string().into()]}))]
3679    #[case(Node::Footnote(Footnote{ident: "id".to_string(), values: vec!["test".to_string().into()], position: None}),
3680           Node::Fragment(Fragment{values: vec!["test".to_string().into()]}))]
3681    #[case(Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec!["test".to_string().into()], position: None}),
3682           Node::Fragment(Fragment{values: vec!["test".to_string().into()]}))]
3683    #[case(Node::TableRow(TableRow{values: vec!["test".to_string().into()], position: None}),
3684           Node::Fragment(Fragment{values: vec!["test".to_string().into()]}))]
3685    #[case(Node::Fragment(Fragment{values: vec!["test".to_string().into()]}),
3686           Node::Fragment(Fragment{values: vec!["test".to_string().into()]}))]
3687    #[case(Node::Text(Text{value: "test".to_string(), position: None}),
3688           Node::Empty)]
3689    #[case(Node::Code(Code{value: "test".to_string(), lang: None, fence: true, meta: None, position: None}),
3690           Node::Empty)]
3691    #[case(Node::Image(Image{alt: "alt".to_string(), url: "url".to_string(), title: None, position: None}),
3692           Node::Empty)]
3693    #[case(Node::Empty, Node::Empty)]
3694    fn test_to_fragment(#[case] node: Node, #[case] expected: Node) {
3695        assert_eq!(node.to_fragment(), expected);
3696    }
3697
3698    #[rstest]
3699    #[case(
3700        &mut Node::Blockquote(Blockquote{values: vec![
3701            Node::Text(Text{value: "old".to_string(), position: None})
3702        ], position: None}),
3703        Node::Fragment(Fragment{values: vec![
3704            Node::Text(Text{value: "new".to_string(), position: None})
3705        ]}),
3706        Node::Blockquote(Blockquote{values: vec![
3707            Node::Text(Text{value: "new".to_string(), position: None})
3708        ], position: None})
3709    )]
3710    #[case(
3711        &mut Node::Delete(Delete{values: vec![
3712            Node::Text(Text{value: "old".to_string(), position: None})
3713        ], position: None}),
3714        Node::Fragment(Fragment{values: vec![
3715            Node::Text(Text{value: "new".to_string(), position: None})
3716        ]}),
3717        Node::Delete(Delete{values: vec![
3718            Node::Text(Text{value: "new".to_string(), position: None})
3719        ], position: None})
3720    )]
3721    #[case(
3722        &mut Node::Emphasis(Emphasis{values: vec![
3723            Node::Text(Text{value: "old".to_string(), position: None})
3724        ], position: None}),
3725        Node::Fragment(Fragment{values: vec![
3726            Node::Text(Text{value: "new".to_string(), position: None})
3727        ]}),
3728        Node::Emphasis(Emphasis{values: vec![
3729            Node::Text(Text{value: "new".to_string(), position: None})
3730        ], position: None})
3731    )]
3732    #[case(
3733        &mut Node::Strong(Strong{values: vec![
3734            Node::Text(Text{value: "old".to_string(), position: None})
3735        ], position: None}),
3736        Node::Fragment(Fragment{values: vec![
3737            Node::Text(Text{value: "new".to_string(), position: None})
3738        ]}),
3739        Node::Strong(Strong{values: vec![
3740            Node::Text(Text{value: "new".to_string(), position: None})
3741        ], position: None})
3742    )]
3743    #[case(
3744        &mut Node::List(List{index: 0, level: 0, checked: None, ordered: false, values: vec![
3745            Node::Text(Text{value: "old".to_string(), position: None})
3746        ], position: None}),
3747        Node::Fragment(Fragment{values: vec![
3748            Node::Text(Text{value: "new".to_string(), position: None})
3749        ]}),
3750        Node::List(List{index: 0, level: 0, checked: None, ordered: false, values: vec![
3751            Node::Text(Text{value: "new".to_string(), position: None})
3752        ], position: None})
3753    )]
3754    #[case(
3755        &mut Node::Heading(Heading{depth: 1, values: vec![
3756            Node::Text(Text{value: "old".to_string(), position: None})
3757        ], position: None}),
3758        Node::Fragment(Fragment{values: vec![
3759            Node::Text(Text{value: "new".to_string(), position: None})
3760        ]}),
3761        Node::Heading(Heading{depth: 1, values: vec![
3762            Node::Text(Text{value: "new".to_string(), position: None})
3763        ], position: None})
3764    )]
3765    #[case(
3766        &mut Node::Link(Link{url: Url("url".to_string()), title: None, values: vec![
3767            Node::Text(Text{value: "old".to_string(), position: None})
3768        ], position: None}),
3769        Node::Fragment(Fragment{values: vec![
3770            Node::Text(Text{value: "new".to_string(), position: None})
3771        ]}),
3772        Node::Link(Link{url: Url("url".to_string()), title: None, values: vec![
3773            Node::Text(Text{value: "new".to_string(), position: None})
3774        ], position: None})
3775    )]
3776    #[case(
3777        &mut Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec![
3778            Node::Text(Text{value: "old".to_string(), position: None})
3779        ], label: None, position: None}),
3780        Node::Fragment(Fragment{values: vec![
3781            Node::Text(Text{value: "new".to_string(), position: None})
3782        ]}),
3783        Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec![
3784            Node::Text(Text{value: "new".to_string(), position: None})
3785        ], label: None, position: None})
3786    )]
3787    #[case(
3788        &mut Node::Footnote(Footnote{ident: "id".to_string(), values: vec![
3789            Node::Text(Text{value: "old".to_string(), position: None})
3790        ], position: None}),
3791        Node::Fragment(Fragment{values: vec![
3792            Node::Text(Text{value: "new".to_string(), position: None})
3793        ]}),
3794        Node::Footnote(Footnote{ident: "id".to_string(), values: vec![
3795            Node::Text(Text{value: "new".to_string(), position: None})
3796        ], position: None})
3797    )]
3798    #[case(
3799        &mut Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![
3800            Node::Text(Text{value: "old".to_string(), position: None})
3801        ], position: None}),
3802        Node::Fragment(Fragment{values: vec![
3803            Node::Text(Text{value: "new".to_string(), position: None})
3804        ]}),
3805        Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![
3806            Node::Text(Text{value: "new".to_string(), position: None})
3807        ], position: None})
3808    )]
3809    #[case(
3810        &mut Node::TableRow(TableRow{values: vec![
3811            Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![
3812                Node::Text(Text{value: "old".to_string(), position: None})
3813            ], position: None})
3814        ], position: None}),
3815        Node::Fragment(Fragment{values: vec![
3816            Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![
3817                Node::Text(Text{value: "new".to_string(), position: None})
3818            ], position: None})
3819        ]}),
3820        Node::TableRow(TableRow{values: vec![
3821            Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![
3822                Node::Text(Text{value: "new".to_string(), position: None})
3823            ], position: None})
3824        ], position: None})
3825    )]
3826    #[case(
3827        &mut Node::Text(Text{value: "old".to_string(), position: None}),
3828        Node::Fragment(Fragment{values: vec![
3829            Node::Text(Text{value: "new".to_string(), position: None})
3830        ]}),
3831        Node::Text(Text{value: "old".to_string(), position: None})
3832    )]
3833    #[case(
3834        &mut Node::Blockquote(Blockquote{values: vec![
3835            Node::Text(Text{value: "text1".to_string(), position: None}),
3836            Node::Text(Text{value: "text2".to_string(), position: None})
3837        ], position: None}),
3838        Node::Fragment(Fragment{values: vec![
3839            Node::Text(Text{value: "new1".to_string(), position: None}),
3840            Node::Text(Text{value: "new2".to_string(), position: None})
3841        ]}),
3842        Node::Blockquote(Blockquote{values: vec![
3843            Node::Text(Text{value: "new1".to_string(), position: None}),
3844            Node::Text(Text{value: "new2".to_string(), position: None})
3845        ], position: None})
3846    )]
3847    #[case(
3848        &mut Node::Strong(Strong{values: vec![
3849            Node::Text(Text{value: "text1".to_string(), position: None}),
3850            Node::Text(Text{value: "text2".to_string(), position: None})
3851        ], position: None}),
3852        Node::Fragment(Fragment{values: vec![
3853            Node::Empty,
3854            Node::Text(Text{value: "new2".to_string(), position: None})
3855        ]}),
3856        Node::Strong(Strong{values: vec![
3857            Node::Text(Text{value: "text1".to_string(), position: None}),
3858            Node::Text(Text{value: "new2".to_string(), position: None})
3859        ], position: None})
3860    )]
3861    #[case(
3862        &mut Node::List(List{index: 0, level: 0, checked: None, ordered: false, values: vec![
3863            Node::Text(Text{value: "text1".to_string(), position: None}),
3864            Node::Text(Text{value: "text2".to_string(), position: None})
3865        ], position: None}),
3866        Node::Fragment(Fragment{values: vec![
3867            Node::Text(Text{value: "new1".to_string(), position: None}),
3868            Node::Fragment(Fragment{values: Vec::new()})
3869        ]}),
3870        Node::List(List{index: 0, level: 0, checked: None, ordered: false, values: vec![
3871            Node::Text(Text{value: "new1".to_string(), position: None}),
3872            Node::Text(Text{value: "text2".to_string(), position: None})
3873        ], position: None})
3874    )]
3875    fn test_apply_fragment(#[case] node: &mut Node, #[case] fragment: Node, #[case] expected: Node) {
3876        node.apply_fragment(fragment);
3877        assert_eq!(*node, expected);
3878    }
3879
3880    #[rstest]
3881    #[case(Node::Text(Text{value: "test".to_string(), position: None}),
3882       Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}},
3883       Node::Text(Text{value: "test".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}))]
3884    #[case(Node::Code(Code{value: "code".to_string(), lang: None, fence: true, meta: None, position: None}),
3885       Position{start: Point{line: 1, column: 1}, end: Point{line: 3, column: 3}},
3886       Node::Code(Code{value: "code".to_string(), lang: None, fence: true, meta: None, position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 3, column: 3}})}))]
3887    #[case(Node::List(List{index: 0, level: 1, checked: None, ordered: false, values: vec![], position: None}),
3888       Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}},
3889       Node::List(List{index: 0, level: 1, checked: None, ordered: false, values: vec![], position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}))]
3890    #[case(Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: None, label: None, position: None}),
3891       Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}},
3892       Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: None, label: None, position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}})}))]
3893    #[case(Node::Delete(Delete{values: vec![Node::Text(Text{value: "test".to_string(), position: None})], position: None}),
3894        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}},
3895        Node::Delete(Delete{values: vec![Node::Text(Text{value: "test".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})})], position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}))]
3896    #[case(Node::Emphasis(Emphasis{values: vec![Node::Text(Text{value: "test".to_string(), position: None})], position: None}),
3897        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}},
3898        Node::Emphasis(Emphasis{values: vec![Node::Text(Text{value: "test".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})})], position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}))]
3899    #[case(Node::Footnote(Footnote{ident: "id".to_string(), values: vec![Node::Text(Text{value: "test".to_string(), position: None})], position: None}),
3900        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}},
3901        Node::Footnote(Footnote{ident: "id".to_string(), values: vec![Node::Text(Text{value: "test".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})})], position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}))]
3902    #[case(Node::FootnoteRef(FootnoteRef{ident: "id".to_string(), label: Some("label".to_string()), position: None}),
3903        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}},
3904        Node::FootnoteRef(FootnoteRef{ident: "id".to_string(), label: Some("label".to_string()), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}))]
3905    #[case(Node::Html(Html{value: "<div>test</div>".to_string(), position: None}),
3906        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 15}},
3907        Node::Html(Html{value: "<div>test</div>".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 15}})}))]
3908    #[case(Node::Yaml(Yaml{value: "key: value".to_string(), position: None}),
3909        Position{start: Point{line: 1, column: 1}, end: Point{line: 3, column: 4}},
3910        Node::Yaml(Yaml{value: "key: value".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 3, column: 4}})}))]
3911    #[case(Node::Toml(Toml{value: "key = \"value\"".to_string(), position: None}),
3912        Position{start: Point{line: 1, column: 1}, end: Point{line: 3, column: 4}},
3913        Node::Toml(Toml{value: "key = \"value\"".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 3, column: 4}})}))]
3914    #[case(Node::Image(Image{alt: "alt".to_string(), url: "url".to_string(), title: None, position: None}),
3915        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 12}},
3916        Node::Image(Image{alt: "alt".to_string(), url: "url".to_string(), title: None, position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 12}})}))]
3917    #[case(Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "id".to_string(), label: None, position: None}),
3918        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}},
3919        Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "id".to_string(), label: None, position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}})}))]
3920    #[case(Node::CodeInline(CodeInline{value: "code".into(), position: None}),
3921        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 7}},
3922        Node::CodeInline(CodeInline{value: "code".into(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 7}})}))]
3923    #[case(Node::MathInline(MathInline{value: "x^2".into(), position: None}),
3924        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}},
3925        Node::MathInline(MathInline{value: "x^2".into(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}))]
3926    #[case(Node::Link(Link{url: Url::new("url".to_string()), title: None, values: vec![Node::Text(Text{value: "text".to_string(), position: None})], position: None}),
3927        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}},
3928        Node::Link(Link{url: Url::new("url".to_string()), title: None, values: vec![Node::Text(Text{value: "text".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}})})], position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}})}))]
3929    #[case(Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec![Node::Text(Text{value: "text".to_string(), position: None})], label: None, position: None}),
3930        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}},
3931        Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec![Node::Text(Text{value: "text".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}})})], label: None, position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}})}))]
3932    #[case(Node::Math(Math{value: "x^2".to_string(), position: None}),
3933        Position{start: Point{line: 1, column: 1}, end: Point{line: 3, column: 3}},
3934        Node::Math(Math{value: "x^2".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 3, column: 3}})}))]
3935    #[case(Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![Node::Text(Text{value: "cell".to_string(), position: None})], position: None}),
3936        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 6}},
3937        Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![Node::Text(Text{value: "cell".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 6}})})], position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 6}})}))]
3938    #[case(Node::TableHeader(TableHeader{align: vec![TableAlignKind::Left, TableAlignKind::Right], position: None}),
3939        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 15}},
3940        Node::TableHeader(TableHeader{align: vec![TableAlignKind::Left, TableAlignKind::Right], position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 15}})}))]
3941    #[case(Node::MdxFlowExpression(MdxFlowExpression{value: "test".into(), position: None}),
3942        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 7}},
3943        Node::MdxFlowExpression(MdxFlowExpression{value: "test".into(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 7}})}))]
3944    #[case(Node::MdxTextExpression(MdxTextExpression{value: "test".into(), position: None}),
3945        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 7}},
3946        Node::MdxTextExpression(MdxTextExpression{value: "test".into(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 7}})}))]
3947    #[case(Node::MdxJsEsm(MdxJsEsm{value: "import React from 'react'".into(), position: None}),
3948        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 25}},
3949        Node::MdxJsEsm(MdxJsEsm{value: "import React from 'react'".into(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 25}})}))]
3950    #[case(Node::MdxJsxTextElement(MdxJsxTextElement{name: Some("span".into()), attributes: Vec::new(), children: vec![Node::Text(Text{value: "text".to_string(), position: None})], position: None}),
3951        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 20}},
3952        Node::MdxJsxTextElement(MdxJsxTextElement{name: Some("span".into()), attributes: Vec::new(), children: vec![Node::Text(Text{value: "text".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 20}})})], position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 20}})}))]
3953    #[case(Node::Break(Break{position: None}),
3954        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 2}},
3955        Node::Break(Break{position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 2}})}))]
3956    #[case(Node::Empty,
3957       Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}},
3958       Node::Empty)]
3959    #[case(Node::Fragment(Fragment{values: vec![
3960           Node::Text(Text{value: "test1".to_string(), position: None}),
3961           Node::Text(Text{value: "test2".to_string(), position: None})
3962       ]}),
3963       Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}},
3964       Node::Fragment(Fragment{values: vec![
3965           Node::Text(Text{value: "test1".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}})}),
3966           Node::Text(Text{value: "test2".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}})})
3967       ]}))]
3968    #[case(Node::Blockquote(Blockquote{values: vec![
3969        Node::Text(Text{value: "test".to_string(), position: None})], position: None}),
3970        Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}},
3971        Node::Blockquote(Blockquote{values: vec![
3972            Node::Text(Text{value: "test".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})})
3973        ], position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}))]
3974    #[case(Node::Heading(Heading{depth: 1, values: vec![
3975            Node::Text(Text{value: "test".to_string(), position: None})], position: None}),
3976            Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}},
3977            Node::Heading(Heading{depth: 1, values: vec![
3978                Node::Text(Text{value: "test".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})})
3979            ], position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}))]
3980    #[case(Node::Strong(Strong{values: vec![
3981            Node::Text(Text{value: "test".to_string(), position: None})], position: None}),
3982            Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}},
3983            Node::Strong(Strong{values: vec![
3984                Node::Text(Text{value: "test".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})})
3985            ], position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 5}})}))]
3986    #[case(Node::TableRow(TableRow{values: vec![
3987            Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![
3988                Node::Text(Text{value: "cell".to_string(), position: None})
3989            ], position: None})
3990        ], position: None}),
3991            Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}},
3992            Node::TableRow(TableRow{values: vec![
3993                Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![
3994                    Node::Text(Text{value: "cell".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}})})
3995                ], position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}})})
3996            ], position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 10}})}))]
3997    #[case(Node::MdxJsxFlowElement(MdxJsxFlowElement{
3998            name: Some("div".to_string()),
3999            attributes: Vec::new(),
4000            children: vec![Node::Text(Text{value: "content".to_string(), position: None})],
4001            position: None
4002        }),
4003            Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 20}},
4004            Node::MdxJsxFlowElement(MdxJsxFlowElement{
4005                name: Some("div".to_string()),
4006                attributes: Vec::new(),
4007                children: vec![Node::Text(Text{value: "content".to_string(), position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 20}})})],
4008                position: Some(Position{start: Point{line: 1, column: 1}, end: Point{line: 1, column: 20}})
4009            }))]
4010    fn test_set_position(#[case] mut node: Node, #[case] position: Position, #[case] expected: Node) {
4011        node.set_position(Some(position));
4012        assert_eq!(node, expected);
4013    }
4014
4015    #[rstest]
4016    #[case(Node::List(List{index: 0, level: 0, checked: None, ordered: false, values: vec!["test".to_string().into()], position: None}), true)]
4017    #[case(Node::List(List{index: 1, level: 2, checked: Some(true), ordered: false, values: vec!["test".to_string().into()], position: None}), true)]
4018    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
4019    fn test_is_list(#[case] node: Node, #[case] expected: bool) {
4020        assert_eq!(node.is_list(), expected);
4021    }
4022
4023    #[rstest]
4024    #[case(Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![], position: None}), true)]
4025    #[case(Node::TableCell(TableCell{column: 1, row: 2, last_cell_in_row: true, last_cell_of_in_table: true, values: vec!["content".to_string().into()], position: None}), true)]
4026    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
4027    fn test_is_table_cell(#[case] node: Node, #[case] expected: bool) {
4028        assert_eq!(node.is_table_cell(), expected);
4029    }
4030
4031    #[rstest]
4032    #[case(Node::TableRow(TableRow{values: vec![], position: None}), true)]
4033    #[case(Node::TableRow(TableRow{values: vec![Node::TableCell(TableCell{column: 0, row: 0, last_cell_in_row: true, last_cell_of_in_table: false, values: vec![], position: None})], position: None}), true)]
4034    #[case(Node::Text(Text{value: "test".to_string(), position: None}), false)]
4035    fn test_is_table_row(#[case] node: Node, #[case] expected: bool) {
4036        assert_eq!(node.is_table_row(), expected);
4037    }
4038
4039    #[rstest]
4040    #[case(Url::new("https://example.com".to_string()), RenderOptions{link_url_style: UrlSurroundStyle::None, ..Default::default()}, "https://example.com")]
4041    #[case(Url::new("https://example.com".to_string()), RenderOptions{link_url_style: UrlSurroundStyle::Angle, ..Default::default()}, "<https://example.com>")]
4042    #[case(Url::new("".to_string()), RenderOptions::default(), "")]
4043    fn test_url_to_string_with(#[case] url: Url, #[case] options: RenderOptions, #[case] expected: &str) {
4044        assert_eq!(url.to_string_with(&options), expected);
4045    }
4046
4047    #[rstest]
4048    #[case(Title::new("title".to_string()), RenderOptions::default(), "\"title\"")]
4049    #[case(Title::new(r#"title with "quotes""#.to_string()), RenderOptions::default(), r#""title with "quotes"""#)]
4050    #[case(Title::new("title with spaces".to_string()), RenderOptions::default(), "\"title with spaces\"")]
4051    #[case(Title::new("".to_string()), RenderOptions::default(), "\"\"")]
4052    #[case(Title::new("title".to_string()), RenderOptions{link_title_style: TitleSurroundStyle::Single, ..Default::default()}, "'title'")]
4053    #[case(Title::new("title with 'quotes'".to_string()), RenderOptions{link_title_style: TitleSurroundStyle::Double, ..Default::default()}, "\"title with 'quotes'\"")]
4054    #[case(Title::new("title".to_string()), RenderOptions{link_title_style: TitleSurroundStyle::Paren, ..Default::default()}, "(title)")]
4055    fn test_title_to_string_with(#[case] title: Title, #[case] options: RenderOptions, #[case] expected: &str) {
4056        assert_eq!(title.to_string_with(&options), expected);
4057    }
4058
4059    #[rstest]
4060    #[case(Node::Fragment(Fragment{values: vec![]}), true)]
4061    #[case(Node::Fragment(Fragment{values: vec![
4062        Node::Text(Text{value: "not_empty".to_string(), position: None})
4063    ]}), false)]
4064    #[case(Node::Fragment(Fragment{values: vec![
4065        Node::Fragment(Fragment{values: vec![]}),
4066        Node::Fragment(Fragment{values: vec![]})
4067    ]}), true)]
4068    #[case(Node::Fragment(Fragment{values: vec![
4069        Node::Fragment(Fragment{values: vec![]}),
4070        Node::Text(Text{value: "not_empty".to_string(), position: None})
4071    ]}), false)]
4072    #[case(Node::Text(Text{value: "not_fragment".to_string(), position: None}), false)]
4073    fn test_is_empty_fragment(#[case] node: Node, #[case] expected: bool) {
4074        assert_eq!(node.is_empty_fragment(), expected);
4075    }
4076
4077    #[rstest]
4078    #[case(Node::Footnote(Footnote{ident: "id".to_string(), values: Vec::new(), position: None}), "ident", Some(AttrValue::String("id".to_string())))]
4079    #[case(Node::Footnote(Footnote{ident: "id".to_string(), values: Vec::new(), position: None}), "unknown", None)]
4080    #[case(Node::Html(Html{value: "<div>test</div>".to_string(), position: None}), "value", Some(AttrValue::String("<div>test</div>".to_string())))]
4081    #[case(Node::Html(Html{value: "<div>test</div>".to_string(), position: None}), "unknown", None)]
4082    #[case(Node::Text(Text{value: "text".to_string(), position: None}), "value", Some(AttrValue::String("text".to_string())))]
4083    #[case(Node::Text(Text{value: "text".to_string(), position: None}), "unknown", None)]
4084    #[case(Node::Code(Code{value: "code".to_string(), lang: Some("rust".to_string()), meta: Some("meta".to_string()), fence: true, position: None}), "value", Some(AttrValue::String("code".to_string())))]
4085    #[case(Node::Code(Code{value: "code".to_string(), lang: Some("rust".to_string()), meta: Some("meta".to_string()), fence: true, position: None}), "lang", Some(AttrValue::String("rust".to_string())))]
4086    #[case(Node::Code(Code{value: "code".to_string(), lang: Some("rust".to_string()), meta: Some("meta".to_string()), fence: true, position: None}), "meta", Some(AttrValue::String("meta".to_string())))]
4087    #[case(Node::Code(Code{value: "code".to_string(), lang: Some("rust".to_string()), meta: Some("meta".to_string()), fence: true, position: None}), "fence", Some(AttrValue::Boolean(true)))]
4088    #[case(Node::Code(Code{value: "code".to_string(), lang: None, meta: None, fence: false, position: None}), "fence", Some(AttrValue::Boolean(false)))]
4089    #[case(Node::CodeInline(CodeInline{value: "inline".into(), position: None}), "value", Some(AttrValue::String("inline".to_string())))]
4090    #[case(Node::MathInline(MathInline{value: "math".into(), position: None}), "value", Some(AttrValue::String("math".to_string())))]
4091    #[case(Node::Math(Math{value: "math".to_string(), position: None}), "value", Some(AttrValue::String("math".to_string())))]
4092    #[case(Node::Yaml(Yaml{value: "yaml".to_string(), position: None}), "value", Some(AttrValue::String("yaml".to_string())))]
4093    #[case(Node::Toml(Toml{value: "toml".to_string(), position: None}), "value", Some(AttrValue::String("toml".to_string())))]
4094    #[case(Node::Image(Image{alt: "alt".to_string(), url: "url".to_string(), title: Some("title".to_string()), position: None}), "alt", Some(AttrValue::String("alt".to_string())))]
4095    #[case(Node::Image(Image{alt: "alt".to_string(), url: "url".to_string(), title: Some("title".to_string()), position: None}), "url", Some(AttrValue::String("url".to_string())))]
4096    #[case(Node::Image(Image{alt: "alt".to_string(), url: "url".to_string(), title: Some("title".to_string()), position: None}), "title", Some(AttrValue::String("title".to_string())))]
4097    #[case(Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "id".to_string(), label: Some("label".to_string()), position: None}), "alt", Some(AttrValue::String("alt".to_string())))]
4098    #[case(Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "id".to_string(), label: Some("label".to_string()), position: None}), "ident", Some(AttrValue::String("id".to_string())))]
4099    #[case(Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "id".to_string(), label: Some("label".to_string()), position: None}), "label", Some(AttrValue::String("label".to_string())))]
4100    #[case(Node::Link(Link{url: Url::new("url".to_string()), title: Some(Title::new("title".to_string())), values: Vec::new(), position: None}), "url", Some(AttrValue::String("url".to_string())))]
4101    #[case(Node::Link(Link{url: Url::new("url".to_string()), title: Some(Title::new("title".to_string())), values: Vec::new(), position: None}), "title", Some(AttrValue::String("title".to_string())))]
4102    #[case(Node::LinkRef(LinkRef{ident: "id".to_string(), values: Vec::new(), label: Some("label".to_string()), position: None}), "ident", Some(AttrValue::String("id".to_string())))]
4103    #[case(Node::LinkRef(LinkRef{ident: "id".to_string(), values: Vec::new(), label: Some("label".to_string()), position: None}), "label", Some(AttrValue::String("label".to_string())))]
4104    #[case(Node::FootnoteRef(FootnoteRef{ident: "id".to_string(), label: Some("label".to_string()), position: None}), "ident", Some(AttrValue::String("id".to_string())))]
4105    #[case(Node::FootnoteRef(FootnoteRef{ident: "id".to_string(), label: Some("label".to_string()), position: None}), "label", Some(AttrValue::String("label".to_string())))]
4106    #[case(Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: Some(Title::new("title".to_string())), label: Some("label".to_string()), position: None}), "ident", Some(AttrValue::String("id".to_string())))]
4107    #[case(Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: Some(Title::new("title".to_string())), label: Some("label".to_string()), position: None}), "url", Some(AttrValue::String("url".to_string())))]
4108    #[case(Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: Some(Title::new("title".to_string())), label: Some("label".to_string()), position: None}), "title", Some(AttrValue::String("title".to_string())))]
4109    #[case(Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: Some(Title::new("title".to_string())), label: Some("label".to_string()), position: None}), "label", Some(AttrValue::String("label".to_string())))]
4110    #[case(Node::Heading(Heading{depth: 3, values: Vec::new(), position: None}), "depth", Some(AttrValue::Integer(3)))]
4111    #[case(Node::List(List{index: 2, level: 1, checked: Some(true), ordered: true, values: Vec::new(), position: None}), "index", Some(AttrValue::Integer(2)))]
4112    #[case(Node::List(List{index: 2, level: 1, checked: Some(true), ordered: true, values: Vec::new(), position: None}), "level", Some(AttrValue::Integer(1)))]
4113    #[case(Node::List(List{index: 2, level: 1, checked: Some(true), ordered: true, values: Vec::new(), position: None}), "ordered", Some(AttrValue::Boolean(true)))]
4114    #[case(Node::List(List{index: 2, level: 1, checked: Some(true), ordered: true, values: Vec::new(), position: None}), "checked", Some(AttrValue::Boolean(true)))]
4115    #[case(Node::TableCell(TableCell{column: 1, row: 2, last_cell_in_row: true, last_cell_of_in_table: false, values: Vec::new(), position: None}), "column", Some(AttrValue::Integer(1)))]
4116    #[case(Node::TableCell(TableCell{column: 1, row: 2, last_cell_in_row: true, last_cell_of_in_table: false, values: Vec::new(), position: None}), "row", Some(AttrValue::Integer(2)))]
4117    #[case(Node::TableCell(TableCell{column: 1, row: 2, last_cell_in_row: true, last_cell_of_in_table: false, values: Vec::new(), position: None}), "last_cell_in_row", Some(AttrValue::Boolean(true)))]
4118    #[case(Node::TableCell(TableCell{column: 1, row: 2, last_cell_in_row: true, last_cell_of_in_table: false, values: Vec::new(), position: None}), "last_cell_of_in_table", Some(AttrValue::Boolean(false)))]
4119    #[case(Node::TableHeader(TableHeader{align: vec![TableAlignKind::Left, TableAlignKind::Right], position: None}), "align", Some(AttrValue::String(":---,---:".to_string())))]
4120    #[case(Node::MdxFlowExpression(MdxFlowExpression{value: "expr".into(), position: None}), "value", Some(AttrValue::String("expr".to_string())))]
4121    #[case(Node::MdxTextExpression(MdxTextExpression{value: "expr".into(), position: None}), "value", Some(AttrValue::String("expr".to_string())))]
4122    #[case(Node::MdxJsEsm(MdxJsEsm{value: "esm".into(), position: None}), "value", Some(AttrValue::String("esm".to_string())))]
4123    #[case(Node::MdxJsxFlowElement(MdxJsxFlowElement{name: Some("div".to_string()), attributes: Vec::new(), children: Vec::new(), position: None}), "name", Some(AttrValue::String("div".to_string())))]
4124    #[case(Node::MdxJsxTextElement(MdxJsxTextElement{name: Some("span".into()), attributes: Vec::new(), children: Vec::new(), position: None}), "name", Some(AttrValue::String("span".to_string())))]
4125    #[case(Node::Break(Break{position: None}), "value", None)]
4126    #[case(Node::HorizontalRule(HorizontalRule{position: None}), "value", None)]
4127    #[case(Node::Fragment(Fragment{values: Vec::new()}), "value", Some(AttrValue::String("".to_string())))]
4128    #[case(Node::Heading(Heading{depth: 1, values: vec![Node::Text(Text{value: "heading text".to_string(), position: None})], position: None}), "value", Some(AttrValue::String("heading text".to_string())))]
4129    #[case(Node::Heading(Heading{depth: 2, values: vec![], position: None}), "value", Some(AttrValue::String("".to_string())))]
4130    #[case(Node::Heading(Heading{depth: 3, values: vec![
4131        Node::Text(Text{value: "first".to_string(), position: None}),
4132        Node::Text(Text{value: "second".to_string(), position: None}),
4133    ], position: None}), "value", Some(AttrValue::String("firstsecond".to_string())))]
4134    #[case(
4135        Node::List(List {
4136            index: 0,
4137            level: 1,
4138            checked: None,
4139            ordered: false,
4140            values: vec![
4141                Node::Text(Text { value: "item1".to_string(), position: None }),
4142                Node::Text(Text { value: "item2".to_string(), position: None }),
4143            ],
4144            position: None,
4145        }),
4146        "value",
4147        Some(AttrValue::String("item1item2".to_string()))
4148    )]
4149    #[case(
4150        Node::TableCell(TableCell {
4151            column: 1,
4152            row: 2,
4153            last_cell_in_row: false,
4154            last_cell_of_in_table: false,
4155            values: vec![Node::Text(Text {
4156                value: "cell_value".to_string(),
4157                position: None,
4158            })],
4159            position: None,
4160        }),
4161        "value",
4162        Some(AttrValue::String("cell_value".to_string()))
4163    )]
4164    #[case::footnote(
4165        Node::Footnote(Footnote {
4166            ident: "id".to_string(),
4167            values: vec![Node::Text(Text {
4168                value: "footnote value".to_string(),
4169                position: None,
4170            })],
4171            position: None,
4172        }),
4173        "value",
4174        Some(AttrValue::String("footnote value".to_string()))
4175    )]
4176    #[case::link(
4177        Node::Link(Link {
4178            url: Url::new("https://example.com".to_string()),
4179            title: Some(Title::new("Example".to_string())),
4180            values: vec![Node::Text(Text {
4181                value: "link text".to_string(),
4182                position: None,
4183            })],
4184            position: None,
4185        }),
4186        "value",
4187        Some(AttrValue::String("link text".to_string()))
4188    )]
4189    #[case::empty(Node::Empty, "value", None)]
4190    #[case::heading(
4191        Node::Heading(Heading {
4192            depth: 1,
4193            values: vec![
4194            Node::Text(Text {
4195                value: "child1".to_string(),
4196                position: None,
4197            }),
4198            Node::Text(Text {
4199                value: "child2".to_string(),
4200                position: None,
4201            }),
4202            ],
4203            position: None,
4204        }),
4205        "children",
4206        Some(AttrValue::Array(vec![
4207            Node::Text(Text {
4208            value: "child1".to_string(),
4209            position: None,
4210            }),
4211            Node::Text(Text {
4212            value: "child2".to_string(),
4213            position: None,
4214            }),
4215        ]))
4216        )]
4217    #[case::list(
4218        Node::List(List {
4219            index: 0,
4220            level: 1,
4221            checked: None,
4222            ordered: false,
4223            values: vec![
4224            Node::Text(Text {
4225                value: "item1".to_string(),
4226                position: None,
4227            }),
4228            ],
4229            position: None,
4230        }),
4231        "children",
4232        Some(AttrValue::Array(vec![
4233            Node::Text(Text {
4234            value: "item1".to_string(),
4235            position: None,
4236            }),
4237        ]))
4238        )]
4239    #[case::blockquote(
4240        Node::Blockquote(Blockquote {
4241            values: vec![
4242            Node::Text(Text {
4243                value: "quote".to_string(),
4244                position: None,
4245            }),
4246            ],
4247            position: None,
4248        }),
4249        "cn",
4250        Some(AttrValue::Array(vec![
4251            Node::Text(Text {
4252            value: "quote".to_string(),
4253            position: None,
4254            }),
4255        ]))
4256        )]
4257    #[case::link(
4258        Node::Link(Link {
4259            url: Url::new("url".to_string()),
4260            title: None,
4261            values: vec![
4262            Node::Text(Text {
4263                value: "link".to_string(),
4264                position: None,
4265            }),
4266            ],
4267            position: None,
4268        }),
4269        "values",
4270        Some(AttrValue::Array(vec![
4271            Node::Text(Text {
4272            value: "link".to_string(),
4273            position: None,
4274            }),
4275        ]))
4276        )]
4277    #[case::table_cell(
4278        Node::TableCell(TableCell {
4279            column: 0,
4280            row: 0,
4281            last_cell_in_row: false,
4282            last_cell_of_in_table: false,
4283            values: vec![
4284            Node::Text(Text {
4285                value: "cell".to_string(),
4286                position: None,
4287            }),
4288            ],
4289            position: None,
4290        }),
4291        "children",
4292        Some(AttrValue::Array(vec![
4293            Node::Text(Text {
4294            value: "cell".to_string(),
4295            position: None,
4296            }),
4297        ]))
4298        )]
4299    #[case::strong(
4300        Node::Strong(Strong {
4301            values: vec![
4302            Node::Text(Text {
4303                value: "bold".to_string(),
4304                position: None,
4305            }),
4306            ],
4307            position: None,
4308        }),
4309        "children",
4310        Some(AttrValue::Array(vec![
4311            Node::Text(Text {
4312            value: "bold".to_string(),
4313            position: None,
4314            }),
4315        ]))
4316        )]
4317    #[case::em(
4318        Node::Emphasis(Emphasis {
4319            values: vec![],
4320            position: None,
4321        }),
4322        "children",
4323        Some(AttrValue::Array(vec![]))
4324        )]
4325    fn test_attr(#[case] node: Node, #[case] attr: &str, #[case] expected: Option<AttrValue>) {
4326        assert_eq!(node.attr(attr), expected);
4327    }
4328
4329    #[rstest]
4330    #[case(
4331        Node::Text(Text{value: "old".to_string(), position: None}),
4332        "value",
4333        "new",
4334        Node::Text(Text{value: "new".to_string(), position: None})
4335    )]
4336    #[case(
4337        Node::Code(Code{value: "old".to_string(), lang: Some("rust".to_string()), fence: true, meta: None, position: None}),
4338        "value",
4339        "new_code",
4340        Node::Code(Code{value: "new_code".to_string(), lang: Some("rust".to_string()), fence: true, meta: None, position: None})
4341    )]
4342    #[case(
4343        Node::Code(Code{value: "code".to_string(), lang: Some("rust".to_string()), fence: true, meta: None, position: None}),
4344        "lang",
4345        "python",
4346        Node::Code(Code{value: "code".to_string(), lang: Some("python".to_string()), fence: true, meta: None, position: None})
4347    )]
4348    #[case(
4349        Node::Code(Code{value: "code".to_string(), lang: None, fence: false, meta: None, position: None}),
4350        "fence",
4351        "true",
4352        Node::Code(Code{value: "code".to_string(), lang: None, fence: true, meta: None, position: None})
4353    )]
4354    #[case(
4355        Node::Image(Image{alt: "alt".to_string(), url: "url".to_string(), title: None, position: None}),
4356        "alt",
4357        "new_alt",
4358        Node::Image(Image{alt: "new_alt".to_string(), url: "url".to_string(), title: None, position: None})
4359    )]
4360    #[case(
4361        Node::Image(Image{alt: "alt".to_string(), url: "url".to_string(), title: None, position: None}),
4362        "url",
4363        "new_url",
4364        Node::Image(Image{alt: "alt".to_string(), url: "new_url".to_string(), title: None, position: None})
4365    )]
4366    #[case(
4367        Node::Image(Image{alt: "alt".to_string(), url: "url".to_string(), title: Some("title".to_string()), position: None}),
4368        "title",
4369        "new_title",
4370        Node::Image(Image{alt: "alt".to_string(), url: "url".to_string(), title: Some("new_title".to_string()), position: None})
4371    )]
4372    #[case(
4373        Node::Heading(Heading{depth: 2, values: vec![], position: None}),
4374        "depth",
4375        "3",
4376        Node::Heading(Heading{depth: 3, values: vec![], position: None})
4377    )]
4378    #[case(
4379        Node::List(List{index: 1, level: 2, checked: Some(true), ordered: false, values: vec![], position: None}),
4380        "checked",
4381        "false",
4382        Node::List(List{index: 1, level: 2, checked: Some(false), ordered: false, values: vec![], position: None})
4383    )]
4384    #[case(
4385        Node::List(List{index: 1, level: 2, checked: Some(true), ordered: false, values: vec![], position: None}),
4386        "ordered",
4387        "true",
4388        Node::List(List{index: 1, level: 2, checked: Some(true), ordered: true, values: vec![], position: None})
4389    )]
4390    #[case(
4391        Node::TableCell(TableCell{column: 1, row: 2, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![], position: None}),
4392        "column",
4393        "3",
4394        Node::TableCell(TableCell{column: 3, row: 2, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![], position: None})
4395    )]
4396    #[case(
4397        Node::TableCell(TableCell{column: 1, row: 2, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![], position: None}),
4398        "row",
4399        "5",
4400        Node::TableCell(TableCell{column: 1, row: 5, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![], position: None})
4401    )]
4402    #[case(
4403        Node::TableCell(TableCell{column: 1, row: 2, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![], position: None}),
4404        "last_cell_in_row",
4405        "true",
4406        Node::TableCell(TableCell{column: 1, row: 2, last_cell_in_row: true, last_cell_of_in_table: false, values: vec![], position: None})
4407    )]
4408    #[case(
4409        Node::TableCell(TableCell{column: 1, row: 2, last_cell_in_row: false, last_cell_of_in_table: false, values: vec![], position: None}),
4410        "last_cell_of_in_table",
4411        "true",
4412        Node::TableCell(TableCell{column: 1, row: 2, last_cell_in_row: false, last_cell_of_in_table: true, values: vec![], position: None})
4413    )]
4414    #[case(
4415        Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: None, label: None, position: None}),
4416        "ident",
4417        "new_id",
4418        Node::Definition(Definition{ident: "new_id".to_string(), url: Url::new("url".to_string()), title: None, label: None, position: None})
4419    )]
4420    #[case(
4421        Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: None, label: None, position: None}),
4422        "url",
4423        "new_url",
4424        Node::Definition(Definition{ident: "id".to_string(), url: Url::new("new_url".to_string()), title: None, label: None, position: None})
4425    )]
4426    #[case(
4427        Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: None, label: None, position: None}),
4428        "label",
4429        "new_label",
4430        Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: None, label: Some("new_label".to_string()), position: None})
4431    )]
4432    #[case(
4433        Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: None, label: None, position: None}),
4434        "title",
4435        "new_title",
4436        Node::Definition(Definition{ident: "id".to_string(), url: Url::new("url".to_string()), title: Some(Title::new("new_title".to_string())), label: None, position: None})
4437    )]
4438    #[case(
4439        Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "id".to_string(), label: Some("label".to_string()), position: None}),
4440        "alt",
4441        "new_alt",
4442        Node::ImageRef(ImageRef{alt: "new_alt".to_string(), ident: "id".to_string(), label: Some("label".to_string()), position: None})
4443    )]
4444    #[case(
4445        Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "id".to_string(), label: Some("label".to_string()), position: None}),
4446        "ident",
4447        "new_id",
4448        Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "new_id".to_string(), label: Some("label".to_string()), position: None})
4449    )]
4450    #[case(
4451        Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "id".to_string(), label: Some("label".to_string()), position: None}),
4452        "label",
4453        "new_label",
4454        Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "id".to_string(), label: Some("new_label".to_string()), position: None})
4455    )]
4456    #[case(
4457        Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "id".to_string(), label: None, position: None}),
4458        "label",
4459        "new_label",
4460        Node::ImageRef(ImageRef{alt: "alt".to_string(), ident: "id".to_string(), label: Some("new_label".to_string()), position: None})
4461    )]
4462    #[case(
4463        Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec![], label: Some("label".to_string()), position: None}),
4464        "ident",
4465        "new_id",
4466        Node::LinkRef(LinkRef{ident: "new_id".to_string(), values: vec![], label: Some("label".to_string()), position: None})
4467    )]
4468    #[case(
4469        Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec![], label: Some("label".to_string()), position: None}),
4470        "label",
4471        "new_label",
4472        Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec![], label: Some("new_label".to_string()), position: None})
4473    )]
4474    #[case(
4475        Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec![], label: None, position: None}),
4476        "label",
4477        "new_label",
4478        Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec![], label: Some("new_label".to_string()), position: None})
4479    )]
4480    #[case(
4481        Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec![], label: Some("label".to_string()), position: None}),
4482        "unknown",
4483        "ignored",
4484        Node::LinkRef(LinkRef{ident: "id".to_string(), values: vec![], label: Some("label".to_string()), position: None})
4485    )]
4486    #[case(
4487        Node::FootnoteRef(FootnoteRef{ident: "id".to_string(), label: Some("label".to_string()), position: None}),
4488        "ident",
4489        "new_id",
4490        Node::FootnoteRef(FootnoteRef{ident: "new_id".to_string(), label: Some("label".to_string()), position: None})
4491    )]
4492    #[case(
4493        Node::FootnoteRef(FootnoteRef{ident: "id".to_string(), label: Some("label".to_string()), position: None}),
4494        "label",
4495        "new_label",
4496        Node::FootnoteRef(FootnoteRef{ident: "id".to_string(), label: Some("new_label".to_string()), position: None})
4497    )]
4498    #[case(
4499        Node::FootnoteRef(FootnoteRef{ident: "id".to_string(), label: None, position: None}),
4500        "label",
4501        "new_label",
4502        Node::FootnoteRef(FootnoteRef{ident: "id".to_string(), label: Some("new_label".to_string()), position: None})
4503    )]
4504    #[case(
4505        Node::FootnoteRef(FootnoteRef{ident: "id".to_string(), label: Some("label".to_string()), position: None}),
4506        "unknown",
4507        "ignored",
4508        Node::FootnoteRef(FootnoteRef{ident: "id".to_string(), label: Some("label".to_string()), position: None})
4509    )]
4510    #[case(Node::Empty, "value", "ignored", Node::Empty)]
4511    #[case(
4512        Node::TableHeader(TableHeader{align: vec![TableAlignKind::Left, TableAlignKind::Right], position: None}),
4513        "align",
4514        "---,:---:",
4515        Node::TableHeader(TableHeader{align: vec![TableAlignKind::None, TableAlignKind::Center], position: None})
4516    )]
4517    #[case(
4518        Node::TableHeader(TableHeader{align: vec![], position: None}),
4519        "align",
4520        ":---,---:",
4521        Node::TableHeader(TableHeader{align: vec![TableAlignKind::Left, TableAlignKind::Right], position: None})
4522    )]
4523    #[case(
4524        Node::TableHeader(TableHeader{align: vec![TableAlignKind::Left], position: None}),
4525        "unknown",
4526        "ignored",
4527        Node::TableHeader(TableHeader{align: vec![TableAlignKind::Left], position: None})
4528    )]
4529    #[case(
4530        Node::MdxFlowExpression(MdxFlowExpression{value: "old".into(), position: None}),
4531        "value",
4532        "new_expr",
4533        Node::MdxFlowExpression(MdxFlowExpression{value: "new_expr".into(), position: None})
4534    )]
4535    #[case(
4536        Node::MdxFlowExpression(MdxFlowExpression{value: "expr".into(), position: None}),
4537        "unknown",
4538        "ignored",
4539        Node::MdxFlowExpression(MdxFlowExpression{value: "expr".into(), position: None})
4540    )]
4541    #[case(
4542        Node::MdxTextExpression(MdxTextExpression{value: "old".into(), position: None}),
4543        "value",
4544        "new_expr",
4545        Node::MdxTextExpression(MdxTextExpression{value: "new_expr".into(), position: None})
4546    )]
4547    #[case(
4548        Node::MdxTextExpression(MdxTextExpression{value: "expr".into(), position: None}),
4549        "unknown",
4550        "ignored",
4551        Node::MdxTextExpression(MdxTextExpression{value: "expr".into(), position: None})
4552    )]
4553    #[case(
4554        Node::MdxJsEsm(MdxJsEsm{value: "import x".into(), position: None}),
4555        "value",
4556        "import y",
4557        Node::MdxJsEsm(MdxJsEsm{value: "import y".into(), position: None})
4558    )]
4559    #[case(
4560        Node::MdxJsEsm(MdxJsEsm{value: "import x".into(), position: None}),
4561        "unknown",
4562        "ignored",
4563        Node::MdxJsEsm(MdxJsEsm{value: "import x".into(), position: None})
4564    )]
4565    #[case(
4566        Node::MdxJsxFlowElement(MdxJsxFlowElement{name: Some("div".to_string()), attributes: Vec::new(), children: Vec::new(), position: None}),
4567        "name",
4568        "section",
4569        Node::MdxJsxFlowElement(MdxJsxFlowElement{name: Some("section".to_string()), attributes: Vec::new(), children: Vec::new(), position: None})
4570    )]
4571    #[case(
4572        Node::MdxJsxFlowElement(MdxJsxFlowElement{name: None, attributes: Vec::new(), children: Vec::new(), position: None}),
4573        "name",
4574        "main",
4575        Node::MdxJsxFlowElement(MdxJsxFlowElement{name: Some("main".to_string()), attributes: Vec::new(), children: Vec::new(), position: None})
4576    )]
4577    #[case(
4578        Node::MdxJsxFlowElement(MdxJsxFlowElement{name: Some("div".to_string()), attributes: Vec::new(), children: Vec::new(), position: None}),
4579        "unknown",
4580        "ignored",
4581        Node::MdxJsxFlowElement(MdxJsxFlowElement{name: Some("div".to_string()), attributes: Vec::new(), children: Vec::new(), position: None})
4582    )]
4583    #[case(
4584        Node::MdxJsxTextElement(MdxJsxTextElement{name: Some("span".into()), attributes: Vec::new(), children: Vec::new(), position: None}),
4585        "name",
4586        "b",
4587        Node::MdxJsxTextElement(MdxJsxTextElement{name: Some("b".into()), attributes: Vec::new(), children: Vec::new(), position: None})
4588    )]
4589    #[case(
4590        Node::MdxJsxTextElement(MdxJsxTextElement{name: None, attributes: Vec::new(), children: Vec::new(), position: None}),
4591        "name",
4592        "i",
4593        Node::MdxJsxTextElement(MdxJsxTextElement{name: Some("i".into()), attributes: Vec::new(), children: Vec::new(), position: None})
4594    )]
4595    #[case(
4596        Node::MdxJsxTextElement(MdxJsxTextElement{name: Some("span".into()), attributes: Vec::new(), children: Vec::new(), position: None}),
4597        "unknown",
4598        "ignored",
4599        Node::MdxJsxTextElement(MdxJsxTextElement{name: Some("span".into()), attributes: Vec::new(), children: Vec::new(), position: None})
4600    )]
4601    fn test_set_attr(#[case] mut node: Node, #[case] attr: &str, #[case] value: &str, #[case] expected: Node) {
4602        node.set_attr(attr, value);
4603        assert_eq!(node, expected);
4604    }
4605
4606    #[rstest]
4607    #[case(AttrValue::String("test".to_string()), AttrValue::String("test".to_string()), true)]
4608    #[case(AttrValue::String("test".to_string()), AttrValue::String("other".to_string()), false)]
4609    #[case(AttrValue::Integer(42), AttrValue::Integer(42), true)]
4610    #[case(AttrValue::Integer(42), AttrValue::Integer(0), false)]
4611    #[case(AttrValue::Boolean(true), AttrValue::Boolean(true), true)]
4612    #[case(AttrValue::Boolean(true), AttrValue::Boolean(false), false)]
4613    #[case(AttrValue::String("42".to_string()), AttrValue::Integer(42), false)]
4614    #[case(AttrValue::Boolean(false), AttrValue::Integer(0), false)]
4615    fn test_attr_value_eq(#[case] a: AttrValue, #[case] b: AttrValue, #[case] expected: bool) {
4616        assert_eq!(a == b, expected);
4617    }
4618
4619    #[rstest]
4620    #[case(AttrValue::String("test".to_string()), "test")]
4621    #[case(AttrValue::Integer(42), "42")]
4622    #[case(AttrValue::Boolean(true), "true")]
4623    fn test_attr_value_as_str(#[case] value: AttrValue, #[case] expected: &str) {
4624        assert_eq!(&value.as_string(), expected);
4625    }
4626
4627    #[rstest]
4628    #[case(AttrValue::Integer(42), Some(42))]
4629    #[case(AttrValue::String("42".to_string()), Some(42))]
4630    #[case(AttrValue::Boolean(false), Some(0))]
4631    fn test_attr_value_as_i64(#[case] value: AttrValue, #[case] expected: Option<i64>) {
4632        assert_eq!(value.as_i64(), expected);
4633    }
4634}