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