markdown_ppp/ast_transform/
convenience.rs1use super::transformer::Transformer;
24use crate::ast::*;
25
26pub trait Transform {
28 fn transform_text<F>(self, f: F) -> Self
42 where
43 F: Fn(String) -> String;
44
45 fn transform_image_urls<F>(self, f: F) -> Self
65 where
66 F: Fn(String) -> String;
67
68 fn transform_link_urls<F>(self, f: F) -> Self
88 where
89 F: Fn(String) -> String;
90
91 fn transform_autolink_urls<F>(self, f: F) -> Self
93 where
94 F: Fn(String) -> String;
95
96 fn transform_code<F>(self, f: F) -> Self
98 where
99 F: Fn(String) -> String;
100
101 fn transform_html<F>(self, f: F) -> Self
103 where
104 F: Fn(String) -> String;
105
106 fn transform_with<T: Transformer>(self, transformer: T) -> Self;
108
109 fn transform_if<F>(self, condition: bool, transform: F) -> Self
111 where
112 F: FnOnce(Self) -> Self,
113 Self: Sized;
114}
115
116impl Transform for Document {
117 fn transform_text<F>(self, f: F) -> Self
118 where
119 F: Fn(String) -> String,
120 {
121 let mut transformer = TextTransformer::new(f);
122 transformer.transform_document(self)
123 }
124
125 fn transform_image_urls<F>(self, f: F) -> Self
126 where
127 F: Fn(String) -> String,
128 {
129 let mut transformer = ImageUrlTransformer::new(f);
130 transformer.transform_document(self)
131 }
132
133 fn transform_link_urls<F>(self, f: F) -> Self
134 where
135 F: Fn(String) -> String,
136 {
137 let mut transformer = LinkUrlTransformer::new(f);
138 transformer.transform_document(self)
139 }
140
141 fn transform_autolink_urls<F>(self, f: F) -> Self
142 where
143 F: Fn(String) -> String,
144 {
145 let mut transformer = AutolinkTransformer::new(f);
146 transformer.transform_document(self)
147 }
148
149 fn transform_code<F>(self, f: F) -> Self
150 where
151 F: Fn(String) -> String,
152 {
153 let mut transformer = CodeTransformer::new(f);
154 transformer.transform_document(self)
155 }
156
157 fn transform_html<F>(self, f: F) -> Self
158 where
159 F: Fn(String) -> String,
160 {
161 let mut transformer = HtmlTransformer::new(f);
162 transformer.transform_document(self)
163 }
164
165 fn transform_with<T: Transformer>(self, mut transformer: T) -> Self {
166 transformer.transform_document(self)
167 }
168
169 fn transform_if<F>(self, condition: bool, transform: F) -> Self
170 where
171 F: FnOnce(Self) -> Self,
172 {
173 if condition {
174 transform(self)
175 } else {
176 self
177 }
178 }
179}
180
181struct TextTransformer<F> {
184 func: F,
185}
186
187impl<F> TextTransformer<F>
188where
189 F: Fn(String) -> String,
190{
191 fn new(func: F) -> Self {
192 Self { func }
193 }
194}
195
196impl<F> Transformer for TextTransformer<F>
197where
198 F: Fn(String) -> String,
199{
200 fn transform_inline(&mut self, inline: Inline) -> Inline {
201 match inline {
202 Inline::Text(text) => Inline::Text((self.func)(text)),
203 other => self.walk_transform_inline(other),
204 }
205 }
206}
207
208struct ImageUrlTransformer<F> {
209 func: F,
210}
211
212impl<F> ImageUrlTransformer<F>
213where
214 F: Fn(String) -> String,
215{
216 fn new(func: F) -> Self {
217 Self { func }
218 }
219}
220
221impl<F> Transformer for ImageUrlTransformer<F>
222where
223 F: Fn(String) -> String,
224{
225 fn transform_inline(&mut self, inline: Inline) -> Inline {
226 match inline {
227 Inline::Image(mut image) => {
228 image.destination = (self.func)(image.destination);
229 Inline::Image(image)
230 }
231 other => self.walk_transform_inline(other),
232 }
233 }
234}
235
236struct LinkUrlTransformer<F> {
237 func: F,
238}
239
240impl<F> LinkUrlTransformer<F>
241where
242 F: Fn(String) -> String,
243{
244 fn new(func: F) -> Self {
245 Self { func }
246 }
247}
248
249impl<F> Transformer for LinkUrlTransformer<F>
250where
251 F: Fn(String) -> String,
252{
253 fn transform_inline(&mut self, inline: Inline) -> Inline {
254 match inline {
255 Inline::Link(mut link) => {
256 link.destination = (self.func)(link.destination);
257 link.children = link
258 .children
259 .into_iter()
260 .map(|child| self.transform_inline(child))
261 .collect();
262 Inline::Link(link)
263 }
264 other => self.walk_transform_inline(other),
265 }
266 }
267}
268
269struct AutolinkTransformer<F> {
270 func: F,
271}
272
273impl<F> AutolinkTransformer<F>
274where
275 F: Fn(String) -> String,
276{
277 fn new(func: F) -> Self {
278 Self { func }
279 }
280}
281
282impl<F> Transformer for AutolinkTransformer<F>
283where
284 F: Fn(String) -> String,
285{
286 fn transform_inline(&mut self, inline: Inline) -> Inline {
287 match inline {
288 Inline::Autolink(url) => Inline::Autolink((self.func)(url)),
289 other => self.walk_transform_inline(other),
290 }
291 }
292}
293
294struct CodeTransformer<F> {
295 func: F,
296}
297
298impl<F> CodeTransformer<F>
299where
300 F: Fn(String) -> String,
301{
302 fn new(func: F) -> Self {
303 Self { func }
304 }
305}
306
307impl<F> Transformer for CodeTransformer<F>
308where
309 F: Fn(String) -> String,
310{
311 fn transform_inline(&mut self, inline: Inline) -> Inline {
312 match inline {
313 Inline::Code(code) => Inline::Code((self.func)(code)),
314 other => self.walk_transform_inline(other),
315 }
316 }
317}
318
319struct HtmlTransformer<F> {
320 func: F,
321}
322
323impl<F> HtmlTransformer<F>
324where
325 F: Fn(String) -> String,
326{
327 fn new(func: F) -> Self {
328 Self { func }
329 }
330}
331
332impl<F> Transformer for HtmlTransformer<F>
333where
334 F: Fn(String) -> String,
335{
336 fn transform_inline(&mut self, inline: Inline) -> Inline {
337 match inline {
338 Inline::Html(html) => Inline::Html((self.func)(html)),
339 other => self.walk_transform_inline(other),
340 }
341 }
342
343 fn transform_block(&mut self, block: Block) -> Block {
344 match block {
345 Block::HtmlBlock(html) => Block::HtmlBlock((self.func)(html)),
346 other => self.walk_transform_block(other),
347 }
348 }
349}
350
351pub trait FilterTransform {
353 fn remove_empty_paragraphs(self) -> Self;
355
356 fn remove_empty_text(self) -> Self;
358
359 fn normalize_whitespace(self) -> Self;
361
362 fn filter_blocks<F>(self, predicate: F) -> Self
364 where
365 F: Fn(&Block) -> bool;
366}
367
368impl FilterTransform for Document {
369 fn remove_empty_paragraphs(mut self) -> Self {
370 self.blocks
371 .retain(|block| !matches!(block, Block::Paragraph(inlines) if inlines.is_empty()));
372 self
373 }
374
375 fn remove_empty_text(self) -> Self {
376 let mut transformer = EmptyTextRemover;
377 transformer.transform_document(self)
378 }
379
380 fn normalize_whitespace(self) -> Self {
381 self.transform_text(|text| text.split_whitespace().collect::<Vec<_>>().join(" "))
382 }
383
384 fn filter_blocks<F>(mut self, predicate: F) -> Self
385 where
386 F: Fn(&Block) -> bool,
387 {
388 self.blocks.retain(|block| predicate(block));
389 self
390 }
391}
392
393struct EmptyTextRemover;
394
395impl Transformer for EmptyTextRemover {
396 fn transform_inline(&mut self, inline: Inline) -> Inline {
397 match inline {
398 Inline::Text(text) if text.trim().is_empty() => Inline::Empty,
399 other => self.walk_transform_inline(other),
400 }
401 }
402}