markdown_ppp/ast/
convert.rs

1//! Conversion utilities for AST nodes with user data
2//!
3//! This module provides traits and functions for converting between different
4//! AST representations with and without user data.
5
6use super::generic;
7use super::*;
8
9// ——————————————————————————————————————————————————————————————————————————
10// Conversion traits
11// ——————————————————————————————————————————————————————————————————————————
12
13/// Add user data to an AST node
14pub trait WithData<T>: Sized {
15    /// The type with user data attached
16    type WithDataType;
17
18    /// Add user data to this AST node
19    fn with_data(self, data: T) -> Self::WithDataType;
20
21    /// Add default user data to this AST node
22    fn with_default_data(self) -> Self::WithDataType
23    where
24        T: Default,
25    {
26        self.with_data(T::default())
27    }
28}
29
30/// Remove user data from an AST node
31pub trait StripData<T>: Sized {
32    /// The type without user data
33    type StrippedType;
34
35    /// Remove user data from this AST node
36    fn strip_data(self) -> Self::StrippedType;
37}
38
39/// Transform user data type in an AST node
40/// NOTE: Replaced by visitor-based approach in `map_data_visitor` module
41pub trait MapData<T, U>: Sized {
42    /// The type with the new user data type
43    type MappedType;
44
45    /// Transform user data using the provided function
46    fn map_data<F>(self, f: F) -> Self::MappedType
47    where
48        F: FnMut(T) -> U;
49}
50
51// ——————————————————————————————————————————————————————————————————————————
52// Conversion functions for regular AST -> generic AST
53// ——————————————————————————————————————————————————————————————————————————
54
55impl<T: Default> WithData<T> for Document {
56    type WithDataType = generic::Document<T>;
57
58    fn with_data(self, data: T) -> Self::WithDataType {
59        generic::Document {
60            blocks: self
61                .blocks
62                .into_iter()
63                .map(|b| b.with_data(T::default()))
64                .collect(),
65            user_data: data,
66        }
67    }
68}
69
70impl<T: Default> WithData<T> for Block {
71    type WithDataType = generic::Block<T>;
72
73    fn with_data(self, data: T) -> Self::WithDataType {
74        match self {
75            Block::Paragraph(content) => generic::Block::Paragraph {
76                content: content
77                    .into_iter()
78                    .map(|i| i.with_data(T::default()))
79                    .collect(),
80                user_data: data,
81            },
82            Block::Heading(heading) => generic::Block::Heading(heading.with_data(data)),
83            Block::ThematicBreak => generic::Block::ThematicBreak { user_data: data },
84            Block::BlockQuote(blocks) => generic::Block::BlockQuote {
85                blocks: blocks
86                    .into_iter()
87                    .map(|b| b.with_data(T::default()))
88                    .collect(),
89                user_data: data,
90            },
91            Block::List(list) => generic::Block::List(list.with_data(data)),
92            Block::CodeBlock(code_block) => generic::Block::CodeBlock(code_block.with_data(data)),
93            Block::HtmlBlock(content) => generic::Block::HtmlBlock {
94                content,
95                user_data: data,
96            },
97            Block::Definition(def) => generic::Block::Definition(def.with_data(data)),
98            Block::Table(table) => generic::Block::Table(table.with_data(data)),
99            Block::FootnoteDefinition(footnote) => {
100                generic::Block::FootnoteDefinition(footnote.with_data(data))
101            }
102            Block::GitHubAlert(alert) => generic::Block::GitHubAlert(alert.with_data(data)),
103            Block::Empty => generic::Block::Empty { user_data: data },
104        }
105    }
106}
107
108impl<T: Default> WithData<T> for Inline {
109    type WithDataType = generic::Inline<T>;
110
111    fn with_data(self, data: T) -> Self::WithDataType {
112        match self {
113            Inline::Text(content) => generic::Inline::Text {
114                content,
115                user_data: data,
116            },
117            Inline::LineBreak => generic::Inline::LineBreak { user_data: data },
118            Inline::Code(content) => generic::Inline::Code {
119                content,
120                user_data: data,
121            },
122            Inline::Html(content) => generic::Inline::Html {
123                content,
124                user_data: data,
125            },
126            Inline::Link(link) => generic::Inline::Link(link.with_data(data)),
127            Inline::LinkReference(link_ref) => {
128                generic::Inline::LinkReference(link_ref.with_data(data))
129            }
130            Inline::Image(image) => generic::Inline::Image(image.with_data(data)),
131            Inline::Emphasis(content) => generic::Inline::Emphasis {
132                content: content
133                    .into_iter()
134                    .map(|i| i.with_data(T::default()))
135                    .collect(),
136                user_data: data,
137            },
138            Inline::Strong(content) => generic::Inline::Strong {
139                content: content
140                    .into_iter()
141                    .map(|i| i.with_data(T::default()))
142                    .collect(),
143                user_data: data,
144            },
145            Inline::Strikethrough(content) => generic::Inline::Strikethrough {
146                content: content
147                    .into_iter()
148                    .map(|i| i.with_data(T::default()))
149                    .collect(),
150                user_data: data,
151            },
152            Inline::Autolink(url) => generic::Inline::Autolink {
153                url,
154                user_data: data,
155            },
156            Inline::FootnoteReference(label) => generic::Inline::FootnoteReference {
157                label,
158                user_data: data,
159            },
160            Inline::Empty => generic::Inline::Empty { user_data: data },
161        }
162    }
163}
164
165impl<T: Default> WithData<T> for Heading {
166    type WithDataType = generic::Heading<T>;
167
168    fn with_data(self, data: T) -> Self::WithDataType {
169        generic::Heading {
170            kind: self.kind,
171            content: self
172                .content
173                .into_iter()
174                .map(|i| i.with_data(T::default()))
175                .collect(),
176            user_data: data,
177        }
178    }
179}
180
181impl<T: Default> WithData<T> for List {
182    type WithDataType = generic::List<T>;
183
184    fn with_data(self, data: T) -> Self::WithDataType {
185        generic::List {
186            kind: generic::ListKind::from(self.kind),
187            items: self
188                .items
189                .into_iter()
190                .map(|i| i.with_data(T::default()))
191                .collect(),
192            user_data: data,
193        }
194    }
195}
196
197impl<T: Default> WithData<T> for ListItem {
198    type WithDataType = generic::ListItem<T>;
199
200    fn with_data(self, data: T) -> Self::WithDataType {
201        generic::ListItem {
202            task: self.task,
203            blocks: self
204                .blocks
205                .into_iter()
206                .map(|b| b.with_data(T::default()))
207                .collect(),
208            user_data: data,
209        }
210    }
211}
212
213impl<T: Default> WithData<T> for CodeBlock {
214    type WithDataType = generic::CodeBlock<T>;
215
216    fn with_data(self, data: T) -> Self::WithDataType {
217        generic::CodeBlock {
218            kind: self.kind,
219            literal: self.literal,
220            user_data: data,
221        }
222    }
223}
224
225impl<T: Default> WithData<T> for LinkDefinition {
226    type WithDataType = generic::LinkDefinition<T>;
227
228    fn with_data(self, data: T) -> Self::WithDataType {
229        generic::LinkDefinition {
230            label: self
231                .label
232                .into_iter()
233                .map(|i| i.with_data(T::default()))
234                .collect(),
235            destination: self.destination,
236            title: self.title,
237            user_data: data,
238        }
239    }
240}
241
242impl<T: Default> WithData<T> for Table {
243    type WithDataType = generic::Table<T>;
244
245    fn with_data(self, data: T) -> Self::WithDataType {
246        generic::Table {
247            rows: self
248                .rows
249                .into_iter()
250                .map(|row| {
251                    row.into_iter()
252                        .map(|cell| {
253                            cell.into_iter()
254                                .map(|i| i.with_data(T::default()))
255                                .collect()
256                        })
257                        .collect()
258                })
259                .collect(),
260            alignments: self.alignments,
261            user_data: data,
262        }
263    }
264}
265
266impl<T: Default> WithData<T> for FootnoteDefinition {
267    type WithDataType = generic::FootnoteDefinition<T>;
268
269    fn with_data(self, data: T) -> Self::WithDataType {
270        generic::FootnoteDefinition {
271            label: self.label,
272            blocks: self
273                .blocks
274                .into_iter()
275                .map(|b| b.with_data(T::default()))
276                .collect(),
277            user_data: data,
278        }
279    }
280}
281
282impl<T: Default> WithData<T> for GitHubAlert {
283    type WithDataType = generic::GitHubAlertNode<T>;
284
285    fn with_data(self, data: T) -> Self::WithDataType {
286        generic::GitHubAlertNode {
287            alert_type: self.alert_type,
288            blocks: self
289                .blocks
290                .into_iter()
291                .map(|b| b.with_data(T::default()))
292                .collect(),
293            user_data: data,
294        }
295    }
296}
297
298impl<T: Default> WithData<T> for Link {
299    type WithDataType = generic::Link<T>;
300
301    fn with_data(self, data: T) -> Self::WithDataType {
302        generic::Link {
303            destination: self.destination,
304            title: self.title,
305            children: self
306                .children
307                .into_iter()
308                .map(|i| i.with_data(T::default()))
309                .collect(),
310            user_data: data,
311        }
312    }
313}
314
315impl<T: Default> WithData<T> for Image {
316    type WithDataType = generic::Image<T>;
317
318    fn with_data(self, data: T) -> Self::WithDataType {
319        generic::Image {
320            destination: self.destination,
321            title: self.title,
322            alt: self.alt,
323            user_data: data,
324        }
325    }
326}
327
328impl<T: Default> WithData<T> for LinkReference {
329    type WithDataType = generic::LinkReference<T>;
330
331    fn with_data(self, data: T) -> Self::WithDataType {
332        generic::LinkReference {
333            label: self
334                .label
335                .into_iter()
336                .map(|i| i.with_data(T::default()))
337                .collect(),
338            text: self
339                .text
340                .into_iter()
341                .map(|i| i.with_data(T::default()))
342                .collect(),
343            user_data: data,
344        }
345    }
346}
347
348// ——————————————————————————————————————————————————————————————————————————
349// Conversion functions for generic AST -> regular AST
350// ——————————————————————————————————————————————————————————————————————————
351
352impl<T> StripData<T> for generic::Document<T> {
353    type StrippedType = Document;
354
355    fn strip_data(self) -> Self::StrippedType {
356        Document {
357            blocks: self.blocks.into_iter().map(|b| b.strip_data()).collect(),
358        }
359    }
360}
361
362impl<T> StripData<T> for generic::Block<T> {
363    type StrippedType = Block;
364
365    fn strip_data(self) -> Self::StrippedType {
366        match self {
367            generic::Block::Paragraph { content, .. } => {
368                Block::Paragraph(content.into_iter().map(|i| i.strip_data()).collect())
369            }
370            generic::Block::Heading(heading) => Block::Heading(heading.strip_data()),
371            generic::Block::ThematicBreak { .. } => Block::ThematicBreak,
372            generic::Block::BlockQuote { blocks, .. } => {
373                Block::BlockQuote(blocks.into_iter().map(|b| b.strip_data()).collect())
374            }
375            generic::Block::List(list) => Block::List(list.strip_data()),
376            generic::Block::CodeBlock(code_block) => Block::CodeBlock(code_block.strip_data()),
377            generic::Block::HtmlBlock { content, .. } => Block::HtmlBlock(content),
378            generic::Block::Definition(def) => Block::Definition(def.strip_data()),
379            generic::Block::Table(table) => Block::Table(table.strip_data()),
380            generic::Block::FootnoteDefinition(footnote) => {
381                Block::FootnoteDefinition(footnote.strip_data())
382            }
383            generic::Block::GitHubAlert(alert) => Block::GitHubAlert(alert.strip_data()),
384            generic::Block::Empty { .. } => Block::Empty,
385        }
386    }
387}
388
389impl<T> StripData<T> for generic::Inline<T> {
390    type StrippedType = Inline;
391
392    fn strip_data(self) -> Self::StrippedType {
393        match self {
394            generic::Inline::Text { content, .. } => Inline::Text(content),
395            generic::Inline::LineBreak { .. } => Inline::LineBreak,
396            generic::Inline::Code { content, .. } => Inline::Code(content),
397            generic::Inline::Html { content, .. } => Inline::Html(content),
398            generic::Inline::Link(link) => Inline::Link(link.strip_data()),
399            generic::Inline::LinkReference(link_ref) => {
400                Inline::LinkReference(link_ref.strip_data())
401            }
402            generic::Inline::Image(image) => Inline::Image(image.strip_data()),
403            generic::Inline::Emphasis { content, .. } => {
404                Inline::Emphasis(content.into_iter().map(|i| i.strip_data()).collect())
405            }
406            generic::Inline::Strong { content, .. } => {
407                Inline::Strong(content.into_iter().map(|i| i.strip_data()).collect())
408            }
409            generic::Inline::Strikethrough { content, .. } => {
410                Inline::Strikethrough(content.into_iter().map(|i| i.strip_data()).collect())
411            }
412            generic::Inline::Autolink { url, .. } => Inline::Autolink(url),
413            generic::Inline::FootnoteReference { label, .. } => Inline::FootnoteReference(label),
414            generic::Inline::Empty { .. } => Inline::Empty,
415        }
416    }
417}
418
419impl<T> StripData<T> for generic::Heading<T> {
420    type StrippedType = Heading;
421
422    fn strip_data(self) -> Self::StrippedType {
423        Heading {
424            kind: self.kind,
425            content: self.content.into_iter().map(|i| i.strip_data()).collect(),
426        }
427    }
428}
429
430impl<T> StripData<T> for generic::List<T> {
431    type StrippedType = List;
432
433    fn strip_data(self) -> Self::StrippedType {
434        List {
435            kind: self.kind.into(),
436            items: self.items.into_iter().map(|i| i.strip_data()).collect(),
437        }
438    }
439}
440
441impl<T> StripData<T> for generic::ListItem<T> {
442    type StrippedType = ListItem;
443
444    fn strip_data(self) -> Self::StrippedType {
445        ListItem {
446            task: self.task,
447            blocks: self.blocks.into_iter().map(|b| b.strip_data()).collect(),
448        }
449    }
450}
451
452impl<T> StripData<T> for generic::CodeBlock<T> {
453    type StrippedType = CodeBlock;
454
455    fn strip_data(self) -> Self::StrippedType {
456        CodeBlock {
457            kind: self.kind,
458            literal: self.literal,
459        }
460    }
461}
462
463impl<T> StripData<T> for generic::LinkDefinition<T> {
464    type StrippedType = LinkDefinition;
465
466    fn strip_data(self) -> Self::StrippedType {
467        LinkDefinition {
468            label: self.label.into_iter().map(|i| i.strip_data()).collect(),
469            destination: self.destination,
470            title: self.title,
471        }
472    }
473}
474
475impl<T> StripData<T> for generic::Table<T> {
476    type StrippedType = Table;
477
478    fn strip_data(self) -> Self::StrippedType {
479        Table {
480            rows: self
481                .rows
482                .into_iter()
483                .map(|row| {
484                    row.into_iter()
485                        .map(|cell| cell.into_iter().map(|i| i.strip_data()).collect())
486                        .collect()
487                })
488                .collect(),
489            alignments: self.alignments,
490        }
491    }
492}
493
494impl<T> StripData<T> for generic::FootnoteDefinition<T> {
495    type StrippedType = FootnoteDefinition;
496
497    fn strip_data(self) -> Self::StrippedType {
498        FootnoteDefinition {
499            label: self.label,
500            blocks: self.blocks.into_iter().map(|b| b.strip_data()).collect(),
501        }
502    }
503}
504
505impl<T> StripData<T> for generic::GitHubAlertNode<T> {
506    type StrippedType = GitHubAlert;
507
508    fn strip_data(self) -> Self::StrippedType {
509        GitHubAlert {
510            alert_type: self.alert_type,
511            blocks: self.blocks.into_iter().map(|b| b.strip_data()).collect(),
512        }
513    }
514}
515
516impl<T> StripData<T> for generic::Link<T> {
517    type StrippedType = Link;
518
519    fn strip_data(self) -> Self::StrippedType {
520        Link {
521            destination: self.destination,
522            title: self.title,
523            children: self.children.into_iter().map(|i| i.strip_data()).collect(),
524        }
525    }
526}
527
528impl<T> StripData<T> for generic::Image<T> {
529    type StrippedType = Image;
530
531    fn strip_data(self) -> Self::StrippedType {
532        Image {
533            destination: self.destination,
534            title: self.title,
535            alt: self.alt,
536        }
537    }
538}
539
540impl<T> StripData<T> for generic::LinkReference<T> {
541    type StrippedType = LinkReference;
542
543    fn strip_data(self) -> Self::StrippedType {
544        LinkReference {
545            label: self.label.into_iter().map(|i| i.strip_data()).collect(),
546            text: self.text.into_iter().map(|i| i.strip_data()).collect(),
547        }
548    }
549}
550
551// ——————————————————————————————————————————————————————————————————————————
552// MapData implementations (transform user data type)
553// NOTE: Disabled due to compiler recursion limits
554// ——————————————————————————————————————————————————————————————————————————
555
556/*
557// Temporarily commented out due to recursion limit issues
558impl<T, U> MapData<T, U> for generic::Document<T> {
559    type MappedType = generic::Document<U>;
560
561    fn map_data<F>(self, mut f: F) -> Self::MappedType
562    where
563        F: FnMut(T) -> U,
564    {
565        generic::Document {
566            blocks: self.blocks.into_iter().map(|b| b.map_data(&mut f)).collect(),
567            user_data: f(self.user_data),
568        }
569    }
570}
571
572impl<T, U> MapData<T, U> for generic::Block<T> {
573    type MappedType = generic::Block<U>;
574
575    fn map_data<F>(self, mut f: F) -> Self::MappedType
576    where
577        F: FnMut(T) -> U,
578    {
579        match self {
580            generic::Block::Paragraph { content, user_data } => generic::Block::Paragraph {
581                content: content.into_iter().map(|i| i.map_data(&mut f)).collect(),
582                user_data: f(user_data),
583            },
584            generic::Block::Heading(heading) => generic::Block::Heading(heading.map_data(f)),
585            generic::Block::ThematicBreak { user_data } => generic::Block::ThematicBreak { user_data: f(user_data) },
586            generic::Block::BlockQuote { blocks, user_data } => generic::Block::BlockQuote {
587                blocks: blocks.into_iter().map(|b| b.map_data(&mut f)).collect(),
588                user_data: f(user_data),
589            },
590            generic::Block::List(list) => generic::Block::List(list.map_data(f)),
591            generic::Block::CodeBlock(code_block) => generic::Block::CodeBlock(code_block.map_data(f)),
592            generic::Block::HtmlBlock { content, user_data } => generic::Block::HtmlBlock {
593                content,
594                user_data: f(user_data),
595            },
596            generic::Block::Definition(def) => generic::Block::Definition(def.map_data(f)),
597            generic::Block::Table(table) => generic::Block::Table(table.map_data(f)),
598            generic::Block::FootnoteDefinition(footnote) => generic::Block::FootnoteDefinition(footnote.map_data(f)),
599            generic::Block::GitHubAlert(alert) => generic::Block::GitHubAlert(alert.map_data(f)),
600            generic::Block::Empty { user_data } => generic::Block::Empty { user_data: f(user_data) },
601        }
602    }
603}
604
605impl<T, U> MapData<T, U> for generic::Inline<T> {
606    type MappedType = generic::Inline<U>;
607
608    fn map_data<F>(self, mut f: F) -> Self::MappedType
609    where
610        F: FnMut(T) -> U,
611    {
612        match self {
613            generic::Inline::Text { content, user_data } => generic::Inline::Text {
614                content,
615                user_data: f(user_data),
616            },
617            generic::Inline::LineBreak { user_data } => generic::Inline::LineBreak { user_data: f(user_data) },
618            generic::Inline::Code { content, user_data } => generic::Inline::Code {
619                content,
620                user_data: f(user_data),
621            },
622            generic::Inline::Html { content, user_data } => generic::Inline::Html {
623                content,
624                user_data: f(user_data),
625            },
626            generic::Inline::Link(link) => generic::Inline::Link(link.map_data(f)),
627            generic::Inline::LinkReference(link_ref) => generic::Inline::LinkReference(link_ref.map_data(f)),
628            generic::Inline::Image(image) => generic::Inline::Image(image.map_data(f)),
629            generic::Inline::Emphasis { content, user_data } => generic::Inline::Emphasis {
630                content: content.into_iter().map(|i| i.map_data(&mut f)).collect(),
631                user_data: f(user_data),
632            },
633            generic::Inline::Strong { content, user_data } => generic::Inline::Strong {
634                content: content.into_iter().map(|i| i.map_data(&mut f)).collect(),
635                user_data: f(user_data),
636            },
637            generic::Inline::Strikethrough { content, user_data } => generic::Inline::Strikethrough {
638                content: content.into_iter().map(|i| i.map_data(&mut f)).collect(),
639                user_data: f(user_data),
640            },
641            generic::Inline::Autolink { url, user_data } => generic::Inline::Autolink {
642                url,
643                user_data: f(user_data),
644            },
645            generic::Inline::FootnoteReference { label, user_data } => generic::Inline::FootnoteReference {
646                label,
647                user_data: f(user_data),
648            },
649            generic::Inline::Empty { user_data } => generic::Inline::Empty { user_data: f(user_data) },
650        }
651    }
652}
653
654// Implementation for other types would follow similar patterns...
655// For brevity, I'll implement a few key ones
656
657impl<T, U> MapData<T, U> for generic::Heading<T> {
658    type MappedType = generic::Heading<U>;
659
660    fn map_data<F>(self, mut f: F) -> Self::MappedType
661    where
662        F: FnMut(T) -> U,
663    {
664        generic::Heading {
665            kind: self.kind,
666            content: self.content.into_iter().map(|i| i.map_data(&mut f)).collect(),
667            user_data: f(self.user_data),
668        }
669    }
670}
671
672impl<T, U> MapData<T, U> for generic::List<T> {
673    type MappedType = generic::List<U>;
674
675    fn map_data<F>(self, mut f: F) -> Self::MappedType
676    where
677        F: FnMut(T) -> U,
678    {
679        generic::List {
680            kind: self.kind,
681            items: self.items.into_iter().map(|i| i.map_data(&mut f)).collect(),
682            user_data: f(self.user_data),
683        }
684    }
685}
686
687impl<T, U> MapData<T, U> for generic::ListItem<T> {
688    type MappedType = generic::ListItem<U>;
689
690    fn map_data<F>(self, mut f: F) -> Self::MappedType
691    where
692        F: FnMut(T) -> U,
693    {
694        generic::ListItem {
695            task: self.task,
696            blocks: self.blocks.into_iter().map(|b| b.map_data(&mut f)).collect(),
697            user_data: f(self.user_data),
698        }
699    }
700}
701
702impl<T, U> MapData<T, U> for generic::Link<T> {
703    type MappedType = generic::Link<U>;
704
705    fn map_data<F>(self, mut f: F) -> Self::MappedType
706    where
707        F: FnMut(T) -> U,
708    {
709        generic::Link {
710            destination: self.destination,
711            title: self.title,
712            children: self.children.into_iter().map(|i| i.map_data(&mut f)).collect(),
713            user_data: f(self.user_data),
714        }
715    }
716}
717
718impl<T, U> MapData<T, U> for generic::Image<T> {
719    type MappedType = generic::Image<U>;
720
721    fn map_data<F>(self, mut f: F) -> Self::MappedType
722    where
723        F: FnMut(T) -> U,
724    {
725        generic::Image {
726            destination: self.destination,
727            title: self.title,
728            alt: self.alt,
729            user_data: f(self.user_data),
730        }
731    }
732}
733
734impl<T, U> MapData<T, U> for generic::LinkReference<T> {
735    type MappedType = generic::LinkReference<U>;
736
737    fn map_data<F>(self, mut f: F) -> Self::MappedType
738    where
739        F: FnMut(T) -> U,
740    {
741        generic::LinkReference {
742            label: self.label.into_iter().map(|i| i.map_data(&mut f)).collect(),
743            text: self.text.into_iter().map(|i| i.map_data(&mut f)).collect(),
744            user_data: f(self.user_data),
745        }
746    }
747}
748
749impl<T, U> MapData<T, U> for generic::CodeBlock<T> {
750    type MappedType = generic::CodeBlock<U>;
751
752    fn map_data<F>(self, mut f: F) -> Self::MappedType
753    where
754        F: FnMut(T) -> U,
755    {
756        generic::CodeBlock {
757            kind: self.kind,
758            literal: self.literal,
759            user_data: f(self.user_data),
760        }
761    }
762}
763
764impl<T, U> MapData<T, U> for generic::Table<T> {
765    type MappedType = generic::Table<U>;
766
767    fn map_data<F>(self, mut f: F) -> Self::MappedType
768    where
769        F: FnMut(T) -> U,
770    {
771        generic::Table {
772            rows: self.rows.into_iter().map(|row| {
773                row.into_iter().map(|cell| {
774                    cell.into_iter().map(|i| i.map_data(&mut f)).collect()
775                }).collect()
776            }).collect(),
777            alignments: self.alignments,
778            user_data: f(self.user_data),
779        }
780    }
781}
782
783impl<T, U> MapData<T, U> for generic::FootnoteDefinition<T> {
784    type MappedType = generic::FootnoteDefinition<U>;
785
786    fn map_data<F>(self, mut f: F) -> Self::MappedType
787    where
788        F: FnMut(T) -> U,
789    {
790        generic::FootnoteDefinition {
791            label: self.label,
792            blocks: self.blocks.into_iter().map(|b| b.map_data(&mut f)).collect(),
793            user_data: f(self.user_data),
794        }
795    }
796}
797
798impl<T, U> MapData<T, U> for generic::GitHubAlertNode<T> {
799    type MappedType = generic::GitHubAlertNode<U>;
800
801    fn map_data<F>(self, mut f: F) -> Self::MappedType
802    where
803        F: FnMut(T) -> U,
804    {
805        generic::GitHubAlertNode {
806            alert_type: self.alert_type,
807            blocks: self.blocks.into_iter().map(|b| b.map_data(&mut f)).collect(),
808            user_data: f(self.user_data),
809        }
810    }
811}
812
813impl<T, U> MapData<T, U> for generic::LinkDefinition<T> {
814    type MappedType = generic::LinkDefinition<U>;
815
816    fn map_data<F>(self, mut f: F) -> Self::MappedType
817    where
818        F: FnMut(T) -> U,
819    {
820        generic::LinkDefinition {
821            label: self.label.into_iter().map(|i| i.map_data(&mut f)).collect(),
822            destination: self.destination,
823            title: self.title,
824            user_data: f(self.user_data),
825        }
826    }
827}
828*/
829
830// End of MapData implementations - commented out due to recursion limits
831
832// ——————————————————————————————————————————————————————————————————————————
833// Helper functions
834// ——————————————————————————————————————————————————————————————————————————
835
836/// Convert from regular AST to generic AST with unit type
837pub fn to_generic(doc: Document) -> generic::Document<()> {
838    doc.with_default_data()
839}
840
841/// Convert from generic AST with unit type to regular AST
842pub fn from_generic(doc: generic::Document<()>) -> Document {
843    doc.strip_data()
844}
845
846// Conversion between ListKind variants
847impl From<ListKind> for generic::ListKind {
848    fn from(kind: ListKind) -> Self {
849        match kind {
850            ListKind::Ordered(opts) => generic::ListKind::Ordered(opts),
851            ListKind::Bullet(bullet) => generic::ListKind::Bullet(bullet),
852        }
853    }
854}
855
856impl From<generic::ListKind> for ListKind {
857    fn from(kind: generic::ListKind) -> Self {
858        match kind {
859            generic::ListKind::Ordered(opts) => ListKind::Ordered(opts),
860            generic::ListKind::Bullet(bullet) => ListKind::Bullet(bullet),
861        }
862    }
863}