pyo3_stub_gen/docgen/
render.rs

1//! JSON rendering and Sphinx extension embedding
2
3use crate::docgen::ir::DocPackage;
4use crate::Result;
5use std::path::Path;
6
7/// Render DocPackage to JSON string
8pub fn render_to_json(package: &DocPackage) -> Result<String> {
9    // Normalize for deterministic output
10    let mut normalized = package.clone();
11    normalized.normalize();
12    Ok(serde_json::to_string_pretty(&normalized)?)
13}
14
15/// Copy the embedded Sphinx extension to the output directory
16pub fn copy_sphinx_extension(output_dir: &Path) -> Result<()> {
17    let extension_code = include_str!("sphinx_ext.py");
18    let ext_path = output_dir.join("pyo3_stub_gen_ext.py");
19    std::fs::write(ext_path, extension_code)?;
20    Ok(())
21}
22
23/// Generate RST files for each module
24pub fn generate_module_pages(package: &DocPackage, output_dir: &Path) -> Result<()> {
25    // Sort modules to ensure consistent ordering
26    let mut module_names: Vec<_> = package.modules.keys().collect();
27    module_names.sort();
28
29    for module_name in module_names {
30        let rst_content = format!(
31            "{}\n{}\n\n.. pyo3-api:: {}\n",
32            module_name,
33            "=".repeat(module_name.len()),
34            module_name
35        );
36
37        // Convert module name to filename: mixed.main_mod -> mixed.main_mod.rst
38        let filename = format!("{}.rst", module_name);
39        let file_path = output_dir.join(&filename);
40
41        std::fs::write(file_path, rst_content)?;
42    }
43
44    Ok(())
45}
46
47/// Generate index.rst that references all module pages
48pub fn generate_index_rst(
49    package: &DocPackage,
50    output_dir: &Path,
51    config: &crate::docgen::config::DocGenConfig,
52) -> Result<()> {
53    let mut content = String::new();
54
55    // Title - use configured title or default to "{package_name} API Reference"
56    let title = if let Some(custom_title) = &config.index_title {
57        if custom_title.is_empty() {
58            "API Reference".to_string()
59        } else {
60            custom_title.clone()
61        }
62    } else {
63        format!("{} API Reference", package.name)
64    };
65
66    content.push_str(&format!("{}\n{}\n\n", title, "=".repeat(title.len())));
67
68    // Add intro message (configurable or default)
69    if let Some(intro) = &config.intro_message {
70        if !intro.is_empty() {
71            content.push_str(intro);
72            content.push_str("\n\n");
73        }
74        // Empty string -> skip intro entirely
75    } else {
76        // Default message when not configured
77        content.push_str(
78            "This is the API reference documentation generated from Rust code using `pyo3-stub-gen <https://github.com/Jij-Inc/pyo3-stub-gen>`_.\n\n",
79        );
80    }
81
82    // Create toctree
83    content.push_str(".. toctree::\n");
84    content.push_str("   :maxdepth: 2\n");
85    content.push_str("   :caption: Modules:\n\n");
86
87    // Sort modules to ensure consistent ordering
88    let mut module_names: Vec<_> = package.modules.keys().collect();
89    module_names.sort();
90
91    for module_name in module_names {
92        content.push_str(&format!("   {}\n", module_name));
93    }
94
95    let index_path = output_dir.join("index.rst");
96    std::fs::write(index_path, content)?;
97
98    Ok(())
99}