pyo3_stub_gen/docgen/
render.rs1use crate::docgen::config::DocGenConfig;
4use crate::docgen::ir::{DocItem, DocPackage};
5use crate::Result;
6use std::path::Path;
7
8pub fn render_to_json(package: &DocPackage) -> Result<String> {
10 let mut normalized = package.clone();
12 normalized.normalize();
13 Ok(serde_json::to_string_pretty(&normalized)?)
14}
15
16pub fn copy_sphinx_extension(output_dir: &Path) -> Result<()> {
18 let extension_code = include_str!("sphinx_ext.py");
19 let ext_path = output_dir.join("pyo3_stub_gen_ext.py");
20 std::fs::write(ext_path, extension_code)?;
21 Ok(())
22}
23
24pub fn generate_module_pages(
26 package: &DocPackage,
27 output_dir: &Path,
28 config: &DocGenConfig,
29) -> Result<()> {
30 let mut module_names: Vec<_> = package.modules.keys().collect();
32 module_names.sort();
33
34 for module_name in module_names {
35 let module = &package.modules[module_name];
36
37 let mut rst_content = format!("{}\n{}\n\n", module_name, "=".repeat(module_name.len()),);
38
39 if config.separate_items {
40 rst_content.push_str(&format!(".. pyo3-api-summary:: {}\n\n", module_name));
42
43 let item_pages: Vec<String> = module
45 .items
46 .iter()
47 .filter_map(|item| {
48 let name = match item {
49 DocItem::Class(c) => &c.name,
50 DocItem::Function(f) => &f.name,
51 DocItem::TypeAlias(t) => &t.name,
52 DocItem::Variable(v) => &v.name,
53 DocItem::Module(_) => return None,
54 };
55 Some(format!(" _items/{}.{}", module_name, name))
56 })
57 .collect();
58
59 if !item_pages.is_empty() {
61 rst_content.push_str(".. toctree::\n");
62 rst_content.push_str(" :hidden:\n\n");
63 for page in &item_pages {
64 rst_content.push_str(page);
65 rst_content.push('\n');
66 }
67 }
68 } else {
69 rst_content.push_str(&format!(".. pyo3-api:: {}\n", module_name));
70 }
71
72 let filename = format!("{}.rst", module_name);
74 let file_path = output_dir.join(&filename);
75
76 std::fs::write(file_path, rst_content)?;
77 }
78
79 Ok(())
80}
81
82pub fn generate_item_pages(package: &DocPackage, output_dir: &Path) -> Result<()> {
90 let items_dir = output_dir.join("_items");
91 if items_dir.exists() {
92 std::fs::remove_dir_all(&items_dir)?;
93 }
94 std::fs::create_dir_all(&items_dir)?;
95
96 for (module_name, module) in &package.modules {
97 for item in &module.items {
98 let (item_name, directive) = match item {
99 DocItem::Class(c) => (c.name.as_str(), "pyo3-api-class"),
100 DocItem::Function(f) => (f.name.as_str(), "pyo3-api-function"),
101 DocItem::TypeAlias(t) => (t.name.as_str(), "pyo3-api-type-alias"),
102 DocItem::Variable(v) => (v.name.as_str(), "pyo3-api-variable"),
103 DocItem::Module(_) => continue,
104 };
105
106 let rst_content = format!(
107 "{}\n{}\n\n.. {}:: {} {}\n",
108 item_name,
109 "=".repeat(item_name.len()),
110 directive,
111 module_name,
112 item_name,
113 );
114
115 let filename = format!("{}.{}.rst", module_name, item_name);
116 std::fs::write(items_dir.join(&filename), rst_content)?;
117 }
118 }
119
120 Ok(())
121}
122
123pub fn generate_index_rst(
125 package: &DocPackage,
126 output_dir: &Path,
127 config: &DocGenConfig,
128) -> Result<()> {
129 let mut content = String::new();
130
131 let title = if let Some(custom_title) = &config.index_title {
133 if custom_title.is_empty() {
134 "API Reference".to_string()
135 } else {
136 custom_title.clone()
137 }
138 } else {
139 format!("{} API Reference", package.name)
140 };
141
142 content.push_str(&format!("{}\n{}\n\n", title, "=".repeat(title.len())));
143
144 if let Some(intro) = &config.intro_message {
146 if !intro.is_empty() {
147 content.push_str(intro);
148 content.push_str("\n\n");
149 }
150 } else {
152 content.push_str(
154 "This is the API reference documentation generated from Rust code using `pyo3-stub-gen <https://github.com/Jij-Inc/pyo3-stub-gen>`_.\n\n",
155 );
156 }
157
158 content.push_str(".. toctree::\n");
160 content.push_str(" :maxdepth: 2\n");
161 content.push_str(" :caption: Modules:\n\n");
162
163 let mut module_names: Vec<_> = package.modules.keys().collect();
165 module_names.sort();
166
167 for module_name in module_names {
168 content.push_str(&format!(" {}\n", module_name));
169 }
170
171 let index_path = output_dir.join("index.rst");
172 std::fs::write(index_path, content)?;
173
174 Ok(())
175}