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
90impl Query for Document {
91 fn find_all_inlines<F>(&self, predicate: F) -> Vec<&Inline>
92 where
93 F: Fn(&Inline) -> bool,
94 {
95 let mut results = Vec::new();
96 for block in &self.blocks {
97 results.extend(block.find_all_inlines(&predicate));
98 }
99 results
100 }
101
102 fn find_all_blocks<F>(&self, predicate: F) -> Vec<&Block>
103 where
104 F: Fn(&Block) -> bool,
105 {
106 let mut results = Vec::new();
107 for block in &self.blocks {
108 results.extend(block.find_all_blocks(&predicate));
109 }
110 results
111 }
112
113 fn find_first_inline<F>(&self, predicate: F) -> Option<&Inline>
114 where
115 F: Fn(&Inline) -> bool,
116 {
117 for block in &self.blocks {
118 if let Some(inline) = block.find_first_inline(&predicate) {
119 return Some(inline);
120 }
121 }
122 None
123 }
124
125 fn find_first_block<F>(&self, predicate: F) -> Option<&Block>
126 where
127 F: Fn(&Block) -> bool,
128 {
129 for block in &self.blocks {
130 if let Some(found) = block.find_first_block(&predicate) {
131 return Some(found);
132 }
133 }
134 None
135 }
136}
137
138impl Query for Block {
139 fn find_all_inlines<F>(&self, predicate: F) -> Vec<&Inline>
140 where
141 F: Fn(&Inline) -> bool,
142 {
143 let mut results = Vec::new();
144 collect_inlines_from_block(self, &predicate, &mut results);
145 results
146 }
147
148 fn find_all_blocks<F>(&self, predicate: F) -> Vec<&Block>
149 where
150 F: Fn(&Block) -> bool,
151 {
152 let mut results = Vec::new();
153 collect_blocks_from_block(self, &predicate, &mut results);
154 results
155 }
156
157 fn find_first_inline<F>(&self, predicate: F) -> Option<&Inline>
158 where
159 F: Fn(&Inline) -> bool,
160 {
161 find_first_inline_in_block(self, &predicate)
162 }
163
164 fn find_first_block<F>(&self, predicate: F) -> Option<&Block>
165 where
166 F: Fn(&Block) -> bool,
167 {
168 find_first_block_in_block(self, &predicate)
169 }
170}
171
172impl Query for Vec<Inline> {
173 fn find_all_inlines<F>(&self, predicate: F) -> Vec<&Inline>
174 where
175 F: Fn(&Inline) -> bool,
176 {
177 let mut results = Vec::new();
178 for inline in self {
179 collect_inlines_from_inline(inline, &predicate, &mut results);
180 }
181 results
182 }
183
184 fn find_all_blocks<F>(&self, _predicate: F) -> Vec<&Block>
185 where
186 F: Fn(&Block) -> bool,
187 {
188 Vec::new() }
190
191 fn find_first_inline<F>(&self, predicate: F) -> Option<&Inline>
192 where
193 F: Fn(&Inline) -> bool,
194 {
195 for inline in self {
196 if let Some(found) = find_first_inline_in_inline(inline, &predicate) {
197 return Some(found);
198 }
199 }
200 None
201 }
202
203 fn find_first_block<F>(&self, _predicate: F) -> Option<&Block>
204 where
205 F: Fn(&Block) -> bool,
206 {
207 None }
209}
210
211fn collect_inlines_from_block<'a, F>(block: &'a Block, predicate: &F, results: &mut Vec<&'a Inline>)
214where
215 F: Fn(&Inline) -> bool,
216{
217 match block {
218 Block::Paragraph(inlines) => {
219 for inline in inlines {
220 collect_inlines_from_inline(inline, predicate, results);
221 }
222 }
223 Block::Heading(heading) => {
224 for inline in &heading.content {
225 collect_inlines_from_inline(inline, predicate, results);
226 }
227 }
228 Block::BlockQuote(blocks) => {
229 for block in blocks {
230 collect_inlines_from_block(block, predicate, results);
231 }
232 }
233 Block::List(list) => {
234 for item in &list.items {
235 for block in &item.blocks {
236 collect_inlines_from_block(block, predicate, results);
237 }
238 }
239 }
240 Block::Table(table) => {
241 for row in &table.rows {
242 for cell in row {
243 for inline in cell {
244 collect_inlines_from_inline(inline, predicate, results);
245 }
246 }
247 }
248 }
249 Block::FootnoteDefinition(footnote) => {
250 for block in &footnote.blocks {
251 collect_inlines_from_block(block, predicate, results);
252 }
253 }
254 Block::GitHubAlert(alert) => {
255 for block in &alert.blocks {
256 collect_inlines_from_block(block, predicate, results);
257 }
258 }
259 Block::Definition(def) => {
260 for inline in &def.label {
261 collect_inlines_from_inline(inline, predicate, results);
262 }
263 }
264 _ => {} }
266}
267
268fn collect_inlines_from_inline<'a, F>(
269 inline: &'a Inline,
270 predicate: &F,
271 results: &mut Vec<&'a Inline>,
272) where
273 F: Fn(&Inline) -> bool,
274{
275 if predicate(inline) {
276 results.push(inline);
277 }
278
279 match inline {
280 Inline::Emphasis(inlines) | Inline::Strong(inlines) | Inline::Strikethrough(inlines) => {
281 for inline in inlines {
282 collect_inlines_from_inline(inline, predicate, results);
283 }
284 }
285 Inline::Link(link) => {
286 for inline in &link.children {
287 collect_inlines_from_inline(inline, predicate, results);
288 }
289 }
290 Inline::LinkReference(link_ref) => {
291 for inline in &link_ref.label {
292 collect_inlines_from_inline(inline, predicate, results);
293 }
294 for inline in &link_ref.text {
295 collect_inlines_from_inline(inline, predicate, results);
296 }
297 }
298 _ => {} }
300}
301
302fn collect_blocks_from_block<'a, F>(block: &'a Block, predicate: &F, results: &mut Vec<&'a Block>)
303where
304 F: Fn(&Block) -> bool,
305{
306 if predicate(block) {
307 results.push(block);
308 }
309
310 match block {
311 Block::BlockQuote(blocks) => {
312 for block in blocks {
313 collect_blocks_from_block(block, predicate, results);
314 }
315 }
316 Block::List(list) => {
317 for item in &list.items {
318 for block in &item.blocks {
319 collect_blocks_from_block(block, predicate, results);
320 }
321 }
322 }
323 Block::FootnoteDefinition(footnote) => {
324 for block in &footnote.blocks {
325 collect_blocks_from_block(block, predicate, results);
326 }
327 }
328 Block::GitHubAlert(alert) => {
329 for block in &alert.blocks {
330 collect_blocks_from_block(block, predicate, results);
331 }
332 }
333 _ => {} }
335}
336
337fn find_first_inline_in_block<'a, F>(block: &'a Block, predicate: &F) -> Option<&'a Inline>
338where
339 F: Fn(&Inline) -> bool,
340{
341 match block {
342 Block::Paragraph(inlines) => {
343 for inline in inlines {
344 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
345 return Some(found);
346 }
347 }
348 }
349 Block::Heading(heading) => {
350 for inline in &heading.content {
351 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
352 return Some(found);
353 }
354 }
355 }
356 Block::BlockQuote(blocks) => {
357 for block in blocks {
358 if let Some(found) = find_first_inline_in_block(block, predicate) {
359 return Some(found);
360 }
361 }
362 }
363 Block::List(list) => {
364 for item in &list.items {
365 for block in &item.blocks {
366 if let Some(found) = find_first_inline_in_block(block, predicate) {
367 return Some(found);
368 }
369 }
370 }
371 }
372 Block::Table(table) => {
373 for row in &table.rows {
374 for cell in row {
375 for inline in cell {
376 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
377 return Some(found);
378 }
379 }
380 }
381 }
382 }
383 Block::FootnoteDefinition(footnote) => {
384 for block in &footnote.blocks {
385 if let Some(found) = find_first_inline_in_block(block, predicate) {
386 return Some(found);
387 }
388 }
389 }
390 Block::GitHubAlert(alert) => {
391 for block in &alert.blocks {
392 if let Some(found) = find_first_inline_in_block(block, predicate) {
393 return Some(found);
394 }
395 }
396 }
397 Block::Definition(def) => {
398 for inline in &def.label {
399 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
400 return Some(found);
401 }
402 }
403 }
404 _ => {} }
406 None
407}
408
409fn find_first_inline_in_inline<'a, F>(inline: &'a Inline, predicate: &F) -> Option<&'a Inline>
410where
411 F: Fn(&Inline) -> bool,
412{
413 if predicate(inline) {
414 return Some(inline);
415 }
416
417 match inline {
418 Inline::Emphasis(inlines) | Inline::Strong(inlines) | Inline::Strikethrough(inlines) => {
419 for inline in inlines {
420 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
421 return Some(found);
422 }
423 }
424 }
425 Inline::Link(link) => {
426 for inline in &link.children {
427 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
428 return Some(found);
429 }
430 }
431 }
432 Inline::LinkReference(link_ref) => {
433 for inline in &link_ref.label {
434 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
435 return Some(found);
436 }
437 }
438 for inline in &link_ref.text {
439 if let Some(found) = find_first_inline_in_inline(inline, predicate) {
440 return Some(found);
441 }
442 }
443 }
444 _ => {} }
446 None
447}
448
449fn find_first_block_in_block<'a, F>(block: &'a Block, predicate: &F) -> Option<&'a Block>
450where
451 F: Fn(&Block) -> bool,
452{
453 if predicate(block) {
454 return Some(block);
455 }
456
457 match block {
458 Block::BlockQuote(blocks) => {
459 for block in blocks {
460 if let Some(found) = find_first_block_in_block(block, predicate) {
461 return Some(found);
462 }
463 }
464 }
465 Block::List(list) => {
466 for item in &list.items {
467 for block in &item.blocks {
468 if let Some(found) = find_first_block_in_block(block, predicate) {
469 return Some(found);
470 }
471 }
472 }
473 }
474 Block::FootnoteDefinition(footnote) => {
475 for block in &footnote.blocks {
476 if let Some(found) = find_first_block_in_block(block, predicate) {
477 return Some(found);
478 }
479 }
480 }
481 Block::GitHubAlert(alert) => {
482 for block in &alert.blocks {
483 if let Some(found) = find_first_block_in_block(block, predicate) {
484 return Some(found);
485 }
486 }
487 }
488 _ => {} }
490 None
491}