1use cmark_writer::ast::Node;
4use ecow::EcoString;
5
6use crate::Result;
7use crate::common::{ExternalFrameNode, FigureNode, FormatWriter};
8
9#[derive(Default)]
11pub struct TextWriter {}
12
13impl TextWriter {
14 pub fn new() -> Self {
15 Self {}
16 }
17
18 fn write_node(node: &Node, output: &mut EcoString) -> Result<()> {
19 match node {
20 Node::Document(blocks) => {
21 for block in blocks {
22 Self::write_node(block, output)?;
23 output.push_str("\n");
24 }
25 }
26 Node::Paragraph(inlines) => {
27 for inline in inlines {
28 Self::write_node(inline, output)?;
29 }
30 output.push_str("\n");
31 }
32 Node::Heading {
33 level: _,
34 content,
35 heading_type: _,
36 } => {
37 for inline in content {
38 Self::write_node(inline, output)?;
39 }
40 output.push_str("\n");
41 }
42 Node::BlockQuote(content) => {
43 for block in content {
44 Self::write_node(block, output)?;
45 }
46 }
47 Node::CodeBlock {
48 language: _,
49 content,
50 block_type: _,
51 } => {
52 output.push_str(content);
53 output.push_str("\n\n");
54 }
55 Node::OrderedList { start: _, items } => {
56 for item in items.iter() {
57 match item {
58 cmark_writer::ast::ListItem::Ordered { content, .. }
59 | cmark_writer::ast::ListItem::Unordered { content } => {
60 for block in content {
61 Self::write_node(block, output)?;
62 }
63 }
64 _ => {}
65 }
66 }
67 }
68 Node::UnorderedList(items) => {
69 for item in items {
70 match item {
71 cmark_writer::ast::ListItem::Ordered { content, .. }
72 | cmark_writer::ast::ListItem::Unordered { content } => {
73 for block in content {
74 Self::write_node(block, output)?;
75 }
76 }
77 _ => {}
78 }
79 }
80 }
81 Node::Table {
82 headers,
83 rows,
84 alignments: _,
85 } => {
86 for header in headers {
88 Self::write_node(header, output)?;
89 output.push(' ');
90 }
91 output.push_str("\n");
92
93 for row in rows {
95 for cell in row {
96 Self::write_node(cell, output)?;
97 output.push(' ');
98 }
99 output.push_str("\n");
100 }
101 output.push_str("\n");
102 }
103 Node::Text(text) => {
104 output.push_str(text);
105 }
106 Node::Emphasis(content) | Node::Strong(content) | Node::Strikethrough(content) => {
107 for inline in content {
108 Self::write_node(inline, output)?;
109 }
110 }
111 Node::Link {
112 url: _,
113 title: _,
114 content,
115 } => {
116 for inline in content {
117 Self::write_node(inline, output)?;
118 }
119 }
120 Node::Image {
121 url: _,
122 title: _,
123 alt,
124 } => {
125 if !alt.is_empty() {
126 for inline in alt {
127 Self::write_node(inline, output)?;
128 }
129 }
130 }
131 Node::InlineCode(code) => {
132 output.push_str(code);
133 }
134 Node::HardBreak => {
135 output.push_str("\n");
136 }
137 Node::SoftBreak => {
138 output.push(' ');
139 }
140 Node::ThematicBreak => {
141 output.push_str("\n");
142 }
143 Node::HtmlElement(element) => {
144 for child in &element.children {
145 Self::write_node(child, output)?;
146 }
147 }
148 node if node.is_custom_type::<FigureNode>() => {
149 if let Some(figure_node) = node.as_custom_type::<FigureNode>() {
150 Self::write_node(&figure_node.body, output)?;
151 if !figure_node.caption.is_empty() {
152 output.push_str("\n");
153 output.push_str(&figure_node.caption);
154 }
155 }
156 }
157 node if node.is_custom_type::<ExternalFrameNode>() => {
158 if let Some(external_frame) = node.as_custom_type::<ExternalFrameNode>()
159 && !external_frame.alt_text.is_empty()
160 {
161 output.push_str(&external_frame.alt_text);
162 }
163 }
164 node if node.is_custom_type::<crate::common::HighlightNode>() => {
165 if let Some(highlight) = node.as_custom_type::<crate::common::HighlightNode>() {
166 for child in &highlight.content {
167 Self::write_node(child, output)?;
168 }
169 }
170 }
171 _ => {}
172 }
173 Ok(())
174 }
175}
176
177impl FormatWriter for TextWriter {
178 fn write_eco(&mut self, document: &Node, output: &mut EcoString) -> Result<()> {
179 Self::write_node(document, output)
180 }
181
182 fn write_vec(&mut self, document: &Node) -> Result<Vec<u8>> {
183 let mut output = EcoString::new();
184 Self::write_node(document, &mut output)?;
185 Ok(output.as_str().as_bytes().to_vec())
186 }
187}