markdown_ppp/ast_transform/
query.rs1use crate::ast::*;
34
35pub trait Query {
37 fn find_all_inlines<F>(&self, predicate: F) -> Vec<&Inline>
39 where
40 F: Fn(&Inline) -> bool;
41
42 fn find_all_blocks<F>(&self, predicate: F) -> Vec<&Block>
44 where
45 F: Fn(&Block) -> bool;
46
47 fn find_first_inline<F>(&self, predicate: F) -> Option<&Inline>
49 where
50 F: Fn(&Inline) -> bool;
51
52 fn find_first_block<F>(&self, predicate: F) -> Option<&Block>
54 where
55 F: Fn(&Block) -> bool;
56
57 fn count_inlines<F>(&self, predicate: F) -> usize
59 where
60 F: Fn(&Inline) -> bool,
61 {
62 self.find_all_inlines(predicate).len()
63 }
64
65 fn count_blocks<F>(&self, predicate: F) -> usize
67 where
68 F: Fn(&Block) -> bool,
69 {
70 self.find_all_blocks(predicate).len()
71 }
72
73 fn any_inline<F>(&self, predicate: F) -> bool
75 where
76 F: Fn(&Inline) -> bool,
77 {
78 self.find_first_inline(predicate).is_some()
79 }
80
81 fn any_block<F>(&self, predicate: F) -> bool
83 where
84 F: Fn(&Block) -> bool,
85 {
86 self.find_first_block(predicate).is_some()
87 }
88
89 fn find_all_links(&self) -> Vec<&Link> {
91 self.find_all_inlines(|inline| matches!(inline, Inline::Link(_)))
92 .into_iter()
93 .filter_map(|inline| match inline {
94 Inline::Link(link) => Some(link),
95 _ => None,
96 })
97 .collect()
98 }
99
100 fn find_all_images(&self) -> Vec<&Image> {
102 self.find_all_inlines(|inline| matches!(inline, Inline::Image(_)))
103 .into_iter()
104 .filter_map(|inline| match inline {
105 Inline::Image(image) => Some(image),
106 _ => None,
107 })
108 .collect()
109 }
110
111 fn find_all_headings(&self) -> Vec<&Heading> {
113 self.find_all_blocks(|block| matches!(block, Block::Heading(_)))
114 .into_iter()
115 .filter_map(|block| match block {
116 Block::Heading(heading) => Some(heading),
117 _ => None,
118 })
119 .collect()
120 }
121
122 fn find_all_autolinks(&self) -> Vec<&str> {
124 self.find_all_inlines(|inline| matches!(inline, Inline::Autolink(_)))
125 .into_iter()
126 .filter_map(|inline| match inline {
127 Inline::Autolink(url) => Some(url.as_str()),
128 _ => None,
129 })
130 .collect()
131 }
132
133 fn find_all_text(&self) -> Vec<&str> {
135 self.find_all_inlines(|inline| matches!(inline, Inline::Text(_)))
136 .into_iter()
137 .filter_map(|inline| match inline {
138 Inline::Text(text) => Some(text.as_str()),
139 _ => None,
140 })
141 .collect()
142 }
143
144 fn find_all_code_spans(&self) -> Vec<&str> {
146 self.find_all_inlines(|inline| matches!(inline, Inline::Code(_)))
147 .into_iter()
148 .filter_map(|inline| match inline {
149 Inline::Code(code) => Some(code.as_str()),
150 _ => None,
151 })
152 .collect()
153 }
154
155 fn find_all_code_blocks(&self) -> Vec<&CodeBlock> {
157 self.find_all_blocks(|block| matches!(block, Block::CodeBlock(_)))
158 .into_iter()
159 .filter_map(|block| match block {
160 Block::CodeBlock(code_block) => Some(code_block),
161 _ => None,
162 })
163 .collect()
164 }
165
166 fn find_all_tables(&self) -> Vec<&Table> {
168 self.find_all_blocks(|block| matches!(block, Block::Table(_)))
169 .into_iter()
170 .filter_map(|block| match block {
171 Block::Table(table) => Some(table),
172 _ => None,
173 })
174 .collect()
175 }
176
177 fn find_all_lists(&self) -> Vec<&List> {
179 self.find_all_blocks(|block| matches!(block, Block::List(_)))
180 .into_iter()
181 .filter_map(|block| match block {
182 Block::List(list) => Some(list),
183 _ => None,
184 })
185 .collect()
186 }
187}
188
189impl Query for Document {
190 fn find_all_inlines<F>(&self, predicate: F) -> Vec<&Inline>
191 where
192 F: Fn(&Inline) -> bool,
193 {
194 let mut results = Vec::new();
195 for block in &self.blocks {
196 results.extend(block.find_all_inlines(&predicate));
197 }
198 results
199 }
200
201 fn find_all_blocks<F>(&self, predicate: F) -> Vec<&Block>
202 where
203 F: Fn(&Block) -> bool,
204 {
205 let mut results = Vec::new();
206 for block in &self.blocks {
207 results.extend(block.find_all_blocks(&predicate));
208 }
209 results
210 }
211
212 fn find_first_inline<F>(&self, predicate: F) -> Option<&Inline>
213 where
214 F: Fn(&Inline) -> bool,
215 {
216 for block in &self.blocks {
217 if let Some(inline) = block.find_first_inline(&predicate) {
218 return Some(inline);
219 }
220 }
221 None
222 }
223
224 fn find_first_block<F>(&self, predicate: F) -> Option<&Block>
225 where
226 F: Fn(&Block) -> bool,
227 {
228 for block in &self.blocks {
229 if let Some(found) = block.find_first_block(&predicate) {
230 return Some(found);
231 }
232 }
233 None
234 }
235}
236
237impl Query for Block {
238 fn find_all_inlines<F>(&self, predicate: F) -> Vec<&Inline>
239 where
240 F: Fn(&Inline) -> bool,
241 {
242 let mut results = Vec::new();
243 collect_inlines_from_block(self, &predicate, &mut results);
244 results
245 }
246
247 fn find_all_blocks<F>(&self, predicate: F) -> Vec<&Block>
248 where
249 F: Fn(&Block) -> bool,
250 {
251 let mut results = Vec::new();
252 collect_blocks_from_block(self, &predicate, &mut results);
253 results
254 }
255
256 fn find_first_inline<F>(&self, predicate: F) -> Option<&Inline>
257 where
258 F: Fn(&Inline) -> bool,
259 {
260 find_first_inline_in_block(self, &predicate)
261 }
262
263 fn find_first_block<F>(&self, predicate: F) -> Option<&Block>
264 where
265 F: Fn(&Block) -> bool,
266 {
267 find_first_block_in_block(self, &predicate)
268 }
269}
270
271impl Query for Vec<Inline> {
272 fn find_all_inlines<F>(&self, predicate: F) -> Vec<&Inline>
273 where
274 F: Fn(&Inline) -> bool,
275 {
276 let mut results = Vec::new();
277 for inline in self {
278 collect_inlines_from_inline(inline, &predicate, &mut results);
279 }
280 results
281 }
282
283 fn find_all_blocks<F>(&self, _predicate: F) -> Vec<&Block>
284 where
285 F: Fn(&Block) -> bool,
286 {
287 Vec::new() }
289
290 fn find_first_inline<F>(&self, predicate: F) -> Option<&Inline>
291 where
292 F: Fn(&Inline) -> bool,
293 {
294 for inline in self {
295 if let Some(found) = find_first_inline_in_inline(inline, &predicate) {
296 return Some(found);
297 }
298 }
299 None
300 }
301
302 fn find_first_block<F>(&self, _predicate: F) -> Option<&Block>
303 where
304 F: Fn(&Block) -> bool,
305 {
306 None }
308}
309
310fn collect_inlines_from_block<'a, F>(block: &'a Block, predicate: &F, results: &mut Vec<&'a Inline>)
313where
314 F: Fn(&Inline) -> bool,
315{
316 match block {
317 Block::Paragraph(inlines) => {
318 for inline in inlines {
319 collect_inlines_from_inline(inline, predicate, results);
320 }
321 }
322 Block::Heading(heading) => {
323 for inline in &heading.content {
324 collect_inlines_from_inline(inline, predicate, results);
325 }
326 }
327 Block::BlockQuote(blocks) => {
328 for block in blocks {
329 collect_inlines_from_block(block, predicate, results);
330 }
331 }
332 Block::List(list) => {
333 for item in &list.items {
334 for block in &item.blocks {
335 collect_inlines_from_block(block, predicate, results);
336 }
337 }
338 }
339 Block::Table(table) => {
340 for row in &table.rows {
341 for cell in row {
342 for inline in cell {
343 collect_inlines_from_inline(inline, predicate, results);
344 }
345 }
346 }
347 }
348 Block::FootnoteDefinition(footnote) => {
349 for block in &footnote.blocks {
350 collect_inlines_from_block(block, predicate, results);
351 }
352 }
353 Block::GitHubAlert(alert) => {
354 for block in &alert.blocks {
355 collect_inlines_from_block(block, predicate, results);
356 }
357 }
358 Block::Definition(def) => {
359 for inline in &def.label {
360 collect_inlines_from_inline(inline, predicate, results);
361 }
362 }
363 _ => {} }
365}
366
367fn collect_inlines_from_inline<'a, F>(
368 inline: &'a Inline,
369 predicate: &F,
370 results: &mut Vec<&'a Inline>,
371) where
372 F: Fn(&Inline) -> bool,
373{
374 if predicate(inline) {
375 results.push(inline);
376 }
377
378 match inline {
379 Inline::Emphasis(inlines) | Inline::Strong(inlines) | Inline::Strikethrough(inlines) => {
380 for inline in inlines {
381 collect_inlines_from_inline(inline, predicate, results);
382 }
383 }
384 Inline::Link(link) => {
385 for inline in &link.children {
386 collect_inlines_from_inline(inline, predicate, results);
387 }
388 }
389 Inline::LinkReference(link_ref) => {
390 for inline in &link_ref.label {
391 collect_inlines_from_inline(inline, predicate, results);
392 }
393 for inline in &link_ref.text {
394 collect_inlines_from_inline(inline, predicate, results);
395 }
396 }
397 _ => {} }
399}
400
401fn collect_blocks_from_block<'a, F>(block: &'a Block, predicate: &F, results: &mut Vec<&'a Block>)
402where
403 F: Fn(&Block) -> bool,
404{
405 if predicate(block) {
406 results.push(block);
407 }
408
409 match block {
410 Block::BlockQuote(blocks) => {
411 for block in blocks {
412 collect_blocks_from_block(block, predicate, results);
413 }
414 }
415 Block::List(list) => {
416 for item in &list.items {
417 for block in &item.blocks {
418 collect_blocks_from_block(block, predicate, results);
419 }
420 }
421 }
422 Block::FootnoteDefinition(footnote) => {
423 for block in &footnote.blocks {
424 collect_blocks_from_block(block, predicate, results);
425 }
426 }
427 Block::GitHubAlert(alert) => {
428 for block in &alert.blocks {
429 collect_blocks_from_block(block, predicate, results);
430 }
431 }
432 _ => {} }
434}
435
436fn find_first_inline_in_block<'a, F>(block: &'a Block, predicate: &F) -> Option<&'a Inline>
437where
438 F: Fn(&Inline) -> bool,
439{
440 match block {
441 Block::Paragraph(inlines) => {
442 for inline in inlines {
443 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
444 return Some(found);
445 }
446 }
447 }
448 Block::Heading(heading) => {
449 for inline in &heading.content {
450 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
451 return Some(found);
452 }
453 }
454 }
455 Block::BlockQuote(blocks) => {
456 for block in blocks {
457 if let Some(found) = find_first_inline_in_block(block, predicate) {
458 return Some(found);
459 }
460 }
461 }
462 Block::List(list) => {
463 for item in &list.items {
464 for block in &item.blocks {
465 if let Some(found) = find_first_inline_in_block(block, predicate) {
466 return Some(found);
467 }
468 }
469 }
470 }
471 Block::Table(table) => {
472 for row in &table.rows {
473 for cell in row {
474 for inline in cell {
475 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
476 return Some(found);
477 }
478 }
479 }
480 }
481 }
482 Block::FootnoteDefinition(footnote) => {
483 for block in &footnote.blocks {
484 if let Some(found) = find_first_inline_in_block(block, predicate) {
485 return Some(found);
486 }
487 }
488 }
489 Block::GitHubAlert(alert) => {
490 for block in &alert.blocks {
491 if let Some(found) = find_first_inline_in_block(block, predicate) {
492 return Some(found);
493 }
494 }
495 }
496 Block::Definition(def) => {
497 for inline in &def.label {
498 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
499 return Some(found);
500 }
501 }
502 }
503 _ => {} }
505 None
506}
507
508fn find_first_inline_in_inline<'a, F>(inline: &'a Inline, predicate: &F) -> Option<&'a Inline>
509where
510 F: Fn(&Inline) -> bool,
511{
512 if predicate(inline) {
513 return Some(inline);
514 }
515
516 match inline {
517 Inline::Emphasis(inlines) | Inline::Strong(inlines) | Inline::Strikethrough(inlines) => {
518 for inline in inlines {
519 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
520 return Some(found);
521 }
522 }
523 }
524 Inline::Link(link) => {
525 for inline in &link.children {
526 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
527 return Some(found);
528 }
529 }
530 }
531 Inline::LinkReference(link_ref) => {
532 for inline in &link_ref.label {
533 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
534 return Some(found);
535 }
536 }
537 for inline in &link_ref.text {
538 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
539 return Some(found);
540 }
541 }
542 }
543 _ => {} }
545 None
546}
547
548fn find_first_block_in_block<'a, F>(block: &'a Block, predicate: &F) -> Option<&'a Block>
549where
550 F: Fn(&Block) -> bool,
551{
552 if predicate(block) {
553 return Some(block);
554 }
555
556 match block {
557 Block::BlockQuote(blocks) => {
558 for block in blocks {
559 if let Some(found) = find_first_block_in_block(block, predicate) {
560 return Some(found);
561 }
562 }
563 }
564 Block::List(list) => {
565 for item in &list.items {
566 for block in &item.blocks {
567 if let Some(found) = find_first_block_in_block(block, predicate) {
568 return Some(found);
569 }
570 }
571 }
572 }
573 Block::FootnoteDefinition(footnote) => {
574 for block in &footnote.blocks {
575 if let Some(found) = find_first_block_in_block(block, predicate) {
576 return Some(found);
577 }
578 }
579 }
580 Block::GitHubAlert(alert) => {
581 for block in &alert.blocks {
582 if let Some(found) = find_first_block_in_block(block, predicate) {
583 return Some(found);
584 }
585 }
586 }
587 _ => {} }
589 None
590}