1use comrak::nodes::{AstNode, NodeHtmlBlock, NodeValue};
18use comrak::Arena;
19
20pub fn transform_tables<'a>(root: &'a AstNode<'a>, arena: &'a Arena<'a>) -> usize {
23 let mut tables: Vec<&'a AstNode<'a>> = Vec::new();
27 collect_tables(root, &mut tables);
28
29 for table in &tables {
30 let open = arena.alloc(AstNode::from(NodeValue::HtmlBlock(NodeHtmlBlock {
31 block_type: 0,
32 literal: "<div class=\"docgen-table-scroll\">\n".to_string(),
33 })));
34 let close = arena.alloc(AstNode::from(NodeValue::HtmlBlock(NodeHtmlBlock {
35 block_type: 0,
36 literal: "</div>\n".to_string(),
37 })));
38 table.insert_before(open);
39 table.insert_after(close);
40 }
41
42 tables.len()
43}
44
45fn collect_tables<'a>(node: &'a AstNode<'a>, out: &mut Vec<&'a AstNode<'a>>) {
46 if matches!(node.data.borrow().value, NodeValue::Table(_)) {
47 out.push(node);
48 return;
51 }
52 for child in node.children() {
53 collect_tables(child, out);
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60 use crate::markdown::{comrak_options, format_ast};
61 use comrak::parse_document;
62
63 fn render(md: &str) -> (String, usize) {
64 let opts = comrak_options();
65 let arena = Arena::new();
66 let root = parse_document(&arena, md, &opts);
67 let count = transform_tables(root, &arena);
68 (format_ast(root, &opts), count)
69 }
70
71 const TABLE: &str = "| a | b |\n|---|---|\n| 1 | 2 |\n";
72
73 #[test]
74 fn wraps_a_single_table_exactly_once() {
75 let (html, count) = render(TABLE);
76 assert_eq!(count, 1);
77 assert_eq!(html.matches("docgen-table-scroll").count(), 1);
78 assert!(html.contains("<div class=\"docgen-table-scroll\">"));
79 assert!(html.contains("<table>"));
80 let open = html.find("docgen-table-scroll").unwrap();
82 let table = html.find("<table>").unwrap();
83 let close = html.rfind("</div>").unwrap();
84 let table_end = html.find("</table>").unwrap();
85 assert!(open < table, "wrapper must open before the table");
86 assert!(close > table_end, "wrapper must close after the table");
87 }
88
89 #[test]
90 fn wraps_each_of_several_tables() {
91 let md = format!("{TABLE}\ntext between\n\n{TABLE}");
92 let (html, count) = render(&md);
93 assert_eq!(count, 2);
94 assert_eq!(html.matches("docgen-table-scroll").count(), 2);
95 }
96
97 #[test]
98 fn document_without_a_table_is_unchanged() {
99 let (html, count) = render("# Heading\n\nJust a paragraph.\n");
100 assert_eq!(count, 0);
101 assert!(!html.contains("docgen-table-scroll"));
102 }
103}