1use comrak::nodes::{AstNode, NodeHtmlBlock, NodeValue};
11use docgen_bases::{render_base_source, Corpus, RenderOptions};
12
13pub fn transform_bases<'a>(root: &'a AstNode<'a>, corpus: &Corpus, base_path: &str) -> usize {
16 let opts = RenderOptions {
17 base: base_path.to_string(),
18 default_view_name: String::new(),
19 interactive: true,
20 block_index: 0,
22 };
23 let mut count = 0;
24 transform(root, corpus, &opts, &mut count);
25 count
26}
27
28fn transform<'a>(node: &'a AstNode<'a>, corpus: &Corpus, opts: &RenderOptions, count: &mut usize) {
29 let replacement = {
30 let data = node.data.borrow();
31 if let NodeValue::CodeBlock(cb) = &data.value {
32 let lang = cb.info.split_whitespace().next().unwrap_or("");
33 if lang.eq_ignore_ascii_case("base") {
34 let opts = RenderOptions {
40 block_index: *count,
41 ..opts.clone()
42 };
43 Some(render_base_source(&cb.literal, corpus, &opts))
44 } else {
45 None
46 }
47 } else {
48 None
49 }
50 };
51 if let Some(html) = replacement {
52 node.data.borrow_mut().value = NodeValue::HtmlBlock(NodeHtmlBlock {
53 block_type: 0,
54 literal: html,
55 });
56 *count += 1;
57 return;
58 }
59 for child in node.children() {
60 transform(child, corpus, opts, count);
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use crate::markdown::{comrak_options, format_ast};
68 use comrak::{parse_document, Arena};
69 use docgen_bases::Note;
70
71 fn corpus() -> Corpus {
72 let n = Note {
73 slug: "books/dune".into(),
74 basename: "Dune".into(),
75 name: "Dune.md".into(),
76 path: "books/Dune.md".into(),
77 tags: vec!["book".into()],
78 ..Default::default()
79 };
80 Corpus::new(vec![n])
81 }
82
83 fn render(md: &str, corpus: &Corpus) -> (String, usize) {
84 let arena = Arena::new();
85 let opts = comrak_options();
86 let root = parse_document(&arena, md, &opts);
87 let n = transform_bases(root, corpus, "");
88 (format_ast(root, &opts), n)
89 }
90
91 #[test]
92 fn base_block_becomes_table() {
93 let md = "```base\nfilters:\n and:\n - file.hasTag(\"book\")\nviews:\n - type: table\n order: [file.name]\n```\n";
94 let (html, n) = render(md, &corpus());
95 assert_eq!(n, 1);
96 assert!(html.contains("docgen-base-table"));
97 assert!(html.contains(">Dune<"));
98 assert!(!html.contains("<code")); }
100
101 #[test]
107 fn two_base_blocks_on_a_page_get_distinct_view_ids() {
108 let block = "```base\nviews:\n - type: table\n order: [file.name]\n```\n";
109 let (html, n) = render(&format!("{block}\n{block}"), &corpus());
110 assert_eq!(n, 2);
111 assert!(html.contains("data-base-view=\"0-0\""));
112 assert!(html.contains("data-base-view=\"1-0\""));
113 }
114
115 #[test]
116 fn non_base_code_block_untouched() {
117 let (html, n) = render("```rust\nfn x() {}\n```\n", &corpus());
118 assert_eq!(n, 0);
119 assert!(html.contains("<pre"));
120 }
121
122 #[test]
123 fn malformed_base_yields_error_block_not_panic() {
124 let md = "```base\nfilters: [unclosed\n```\n";
125 let (html, n) = render(md, &corpus());
126 assert_eq!(n, 1);
127 assert!(html.contains("docgen-base-error"));
128 }
129
130 #[test]
131 fn empty_corpus_renders_no_results() {
132 let md = "```base\nviews:\n - type: table\n```\n";
133 let (html, _) = render(md, &Corpus::default());
134 assert!(html.contains("No results"));
135 }
136}