acdc_parser/model/inlines/
converter.rs

1use crate::{InlineMacro, InlineNode};
2
3// TODO(nlopes): this could instead be impl ToString for InlineNode?
4//
5// To do so, we'd need to change Vec<InlineNode> to a newtype struct.
6/// Extract plain text from inline nodes, recursively handling formatted text.
7///
8/// This function recursively extracts text from all inline formatting nodes
9/// (bold, italic, monospace, etc.), which is useful for generating IDs,
10/// alt text, and other plain text representations of formatted content.
11#[allow(clippy::must_use_candidate)]
12pub fn inlines_to_string(inlines: &[InlineNode]) -> String {
13    inlines
14        .iter()
15        .map(|node| match node {
16            InlineNode::PlainText(text) => text.content.clone(),
17            InlineNode::RawText(text) => text.content.clone(),
18            InlineNode::VerbatimText(text) => text.content.clone(),
19            InlineNode::BoldText(bold) => inlines_to_string(&bold.content),
20            InlineNode::ItalicText(italic) => inlines_to_string(&italic.content),
21            InlineNode::MonospaceText(mono) => inlines_to_string(&mono.content),
22            InlineNode::HighlightText(highlight) => inlines_to_string(&highlight.content),
23            InlineNode::SubscriptText(sub) => inlines_to_string(&sub.content),
24            InlineNode::SuperscriptText(sup) => inlines_to_string(&sup.content),
25            InlineNode::CurvedQuotationText(quote) => inlines_to_string(&quote.content),
26            InlineNode::CurvedApostropheText(apos) => inlines_to_string(&apos.content),
27            InlineNode::StandaloneCurvedApostrophe(_) => "'".to_string(),
28            InlineNode::LineBreak(_) => " ".to_string(),
29            InlineNode::InlineAnchor(_) => String::new(),
30            InlineNode::Macro(macro_node) => match macro_node {
31                InlineMacro::Link(link) => {
32                    link.text.clone().unwrap_or_else(|| link.target.to_string())
33                }
34                InlineMacro::Url(url) => {
35                    if url.text.is_empty() {
36                        url.target.to_string()
37                    } else {
38                        inlines_to_string(&url.text)
39                    }
40                }
41                InlineMacro::Mailto(mailto) => {
42                    if mailto.text.is_empty() {
43                        mailto.target.to_string()
44                    } else {
45                        inlines_to_string(&mailto.text)
46                    }
47                }
48                InlineMacro::Autolink(autolink) => autolink.url.to_string(),
49                InlineMacro::CrossReference(xref) => {
50                    xref.text.clone().unwrap_or_else(|| xref.target.clone())
51                }
52                // Skip other macro types (images, footnotes, buttons, icons, etc.)
53                InlineMacro::Image(_)
54                | InlineMacro::Footnote(_)
55                | InlineMacro::Button(_)
56                | InlineMacro::Pass(_)
57                | InlineMacro::Keyboard(_)
58                | InlineMacro::Menu(_)
59                | InlineMacro::Stem(_)
60                | InlineMacro::Icon(_) => String::new(),
61            },
62        })
63        .collect()
64}