Skip to main content

krabka_docgen/
emit.rs

1//! Wrap generated markdown bodies in Zola front matter and write the
2//! reference tree to disk.
3
4use std::path::Path;
5
6/// Front matter for a docs page. It uses the `AdiDoks` docs layout.
7#[must_use]
8pub fn page_front_matter(title: &str, weight: u32, body: &str) -> String {
9    format!(
10        "+++\ntitle = \"{title}\"\nweight = {weight}\ntemplate = \"docs/page.html\"\n+++\n\n{body}"
11    )
12}
13
14/// Front matter for a docs page that also needs an `[extra]` table.
15///
16/// An example is a page that opts into Mermaid rendering with
17/// `extra.mermaid = true`. The `extra_toml` is the raw body of the `[extra]`
18/// table: one `key = value` per line, with no `[extra]` header around it.
19#[must_use]
20pub fn page_front_matter_with_extra(
21    title: &str,
22    weight: u32,
23    extra_toml: &str,
24    body: &str,
25) -> String {
26    format!(
27        "+++\ntitle = \"{title}\"\nweight = {weight}\ntemplate = \"docs/page.html\"\n\n[extra]\n{extra_toml}\n+++\n\n{body}"
28    )
29}
30
31/// Front matter for a docs section index. It uses the `AdiDoks` docs section
32/// layout.
33#[must_use]
34pub fn section_front_matter(title: &str, weight: u32, body: &str) -> String {
35    format!(
36        "+++\ntitle = \"{title}\"\nweight = {weight}\nsort_by = \"weight\"\ntemplate = \"docs/section.html\"\n+++\n\n{body}"
37    )
38}
39
40/// Write the full reference tree under `out_dir`.
41///
42/// The tree holds the broker pages, the concepts pages, and the section
43/// indexes. This function overwrites existing files.
44///
45/// The operator CRD pages are not here. The operator crate lives in
46/// `krabka-io/krabka-operator`, so its reference pages are rendered there.
47///
48/// # Errors
49/// Returns an error if any directory cannot be created or any file cannot be
50/// written.
51pub fn write_reference_tree(out_dir: &Path) -> anyhow::Result<()> {
52    use {
53        crate::{broker, scenarios},
54        std::fs,
55    };
56    let br_dir = out_dir.join("broker");
57    let concepts_dir = out_dir.join("concepts");
58    fs::create_dir_all(&br_dir)?;
59    fs::create_dir_all(&concepts_dir)?;
60    fs::write(
61        out_dir.join("_index.md"),
62        section_front_matter(
63            "Reference",
64            40,
65            "Auto-generated API reference for the Krabka broker.",
66        ),
67    )?;
68    fs::write(
69        br_dir.join("_index.md"),
70        section_front_matter(
71            "Broker",
72            20,
73            "Broker server config, topic configs, and the Kafka protocol API surface.",
74        ),
75    )?;
76    fs::write(
77        br_dir.join("server-config.md"),
78        page_front_matter("Server Configuration", 10, &broker::server_config_md()),
79    )?;
80    fs::write(
81        br_dir.join("topic-configs.md"),
82        page_front_matter("Topic Configs", 20, &broker::topic_configs_md()),
83    )?;
84    fs::write(
85        br_dir.join("protocol-apis.md"),
86        page_front_matter("Protocol APIs", 30, &broker::protocol_apis_md()),
87    )?;
88    fs::write(
89        concepts_dir.join("_index.md"),
90        section_front_matter(
91            "Concepts",
92            40,
93            "How Krabka's consensus core behaves under failures, illustrated with \
94             diagrams generated from the simulator itself.",
95        ),
96    )?;
97    fs::write(
98        concepts_dir.join("failure-scenarios.md"),
99        page_front_matter_with_extra(
100            "Failure Scenarios",
101            10,
102            "mermaid = true",
103            &scenarios::failure_scenarios_md(),
104        ),
105    )?;
106    Ok(())
107}
108
109#[cfg(test)]
110mod tests {
111
112    use {super::*, std::fs, tempfile::tempdir};
113    #[test]
114    fn page_front_matter_wraps_body() {
115        let s = page_front_matter("Kafka", 10, "hello");
116        assert2::assert!(s.starts_with(
117            "+++\ntitle = \"Kafka\"\nweight = 10\ntemplate = \"docs/page.html\"\n+++\n"
118        ));
119        assert2::assert!(s.contains("hello"));
120    }
121    #[test]
122    fn writes_full_tree() {
123        let dir = tempdir().unwrap();
124        write_reference_tree(dir.path()).unwrap();
125        for page in [
126            "broker/protocol-apis.md",
127            "broker/server-config.md",
128            "broker/topic-configs.md",
129            "concepts/failure-scenarios.md",
130        ] {
131            assert2::assert!(dir.path().join(page).exists());
132        }
133        let apis = fs::read_to_string(dir.path().join("broker/protocol-apis.md")).unwrap();
134        assert2::assert!(apis.contains("template = \"docs/page.html\""));
135        let idx = fs::read_to_string(dir.path().join("_index.md")).unwrap();
136        assert2::assert!(idx.contains("template = \"docs/section.html\""));
137    }
138}