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#[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 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 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 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 pub fn is_string(&self) -> bool {
580 matches!(self, AttrValue::String(_))
581 }
582
583 pub fn is_number(&self) -> bool {
585 matches!(self, AttrValue::Number(_))
586 }
587
588 pub fn is_integer(&self) -> bool {
590 matches!(self, AttrValue::Integer(_))
591 }
592
593 pub fn is_boolean(&self) -> bool {
595 matches!(self, AttrValue::Boolean(_))
596 }
597
598 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 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 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(), "")]
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(), "")]
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}