1use anyhow::Result;
2use serde::{Deserialize, Serialize};
3use std::path::PathBuf;
4
5use crate::python_config::PythonConfigParser;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct BuildConfig {
9 pub parallel_jobs: Option<usize>,
11
12 pub max_cache_size_mb: usize,
14
15 pub cache_expiration_hours: u64,
17
18 pub output: OutputConfig,
20
21 pub theme: ThemeConfig,
23
24 pub extensions: Vec<String>,
26
27 pub template_dirs: Vec<PathBuf>,
29
30 pub static_dirs: Vec<PathBuf>,
32
33 pub optimization: OptimizationConfig,
35
36 pub project: String,
39
40 pub version: Option<String>,
42
43 pub release: Option<String>,
45
46 pub copyright: Option<String>,
48
49 pub language: Option<String>,
51
52 pub root_doc: Option<String>,
54
55 pub html_style: Vec<String>,
57
58 pub html_css_files: Vec<String>,
60
61 pub html_js_files: Vec<String>,
63
64 pub html_static_path: Vec<PathBuf>,
66
67 pub html_logo: Option<String>,
69
70 pub html_favicon: Option<String>,
72
73 pub html_title: Option<String>,
75
76 pub html_short_title: Option<String>,
78
79 pub html_show_copyright: Option<bool>,
81
82 pub html_show_sphinx: Option<bool>,
84
85 pub html_copy_source: Option<bool>,
87
88 pub html_show_sourcelink: Option<bool>,
90
91 pub html_sourcelink_suffix: Option<String>,
93
94 pub html_use_index: Option<bool>,
96
97 pub html_use_opensearch: Option<bool>,
99
100 pub html_last_updated_fmt: Option<String>,
102
103 pub templates_path: Vec<PathBuf>,
105
106 pub fail_on_warning: bool,
108
109 pub include_patterns: Vec<String>,
112
113 pub exclude_patterns: Vec<String>,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct OutputConfig {
121 pub html_theme: String,
123
124 pub syntax_highlighting: bool,
126
127 pub highlight_theme: String,
129
130 pub search_index: bool,
132
133 pub minify_html: bool,
135
136 pub compress_output: bool,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct ThemeConfig {
142 pub name: String,
144
145 pub options: serde_json::Value,
147
148 pub custom_css: Vec<PathBuf>,
150
151 pub custom_js: Vec<PathBuf>,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct OptimizationConfig {
157 pub parallel_processing: bool,
159
160 pub incremental_builds: bool,
162
163 pub document_caching: bool,
165
166 pub image_optimization: bool,
168
169 pub asset_bundling: bool,
171}
172
173impl Default for BuildConfig {
174 fn default() -> Self {
175 Self {
176 parallel_jobs: None,
177 max_cache_size_mb: 500,
178 cache_expiration_hours: 24,
179 output: OutputConfig::default(),
180 theme: ThemeConfig::default(),
181 extensions: vec![
182 "sphinx.ext.autodoc".to_string(),
183 "sphinx.ext.viewcode".to_string(),
184 "sphinx.ext.intersphinx".to_string(),
185 ],
186 template_dirs: vec![],
187 static_dirs: vec![],
188 optimization: OptimizationConfig::default(),
189
190 project: "Sphinx Ultra Project".to_string(),
192 version: Some("1.0.0".to_string()),
193 release: Some("1.0.0".to_string()),
194 copyright: Some("2024, Sphinx Ultra".to_string()),
195 language: Some("en".to_string()),
196 root_doc: Some("index".to_string()),
197 html_style: vec!["sphinx_rtd_theme.css".to_string()],
198 html_css_files: vec![],
199 html_js_files: vec![],
200 html_static_path: vec![PathBuf::from("_static")],
201 html_logo: None,
202 html_favicon: None,
203 html_title: None,
204 html_short_title: None,
205 html_show_copyright: Some(true),
206 html_show_sphinx: Some(true),
207 html_copy_source: Some(true),
208 html_show_sourcelink: Some(true),
209 html_sourcelink_suffix: Some(".txt".to_string()),
210 html_use_index: Some(true),
211 html_use_opensearch: Some(false),
212 html_last_updated_fmt: Some("%b %d, %Y".to_string()),
213 templates_path: vec![PathBuf::from("_templates")],
214
215 fail_on_warning: false,
217
218 include_patterns: vec!["**".to_string()],
220 exclude_patterns: vec![],
221 }
222 }
223}
224
225impl Default for OutputConfig {
226 fn default() -> Self {
227 Self {
228 html_theme: "sphinx_rtd_theme".to_string(),
229 syntax_highlighting: true,
230 highlight_theme: "github".to_string(),
231 search_index: true,
232 minify_html: false,
233 compress_output: false,
234 }
235 }
236}
237
238impl Default for ThemeConfig {
239 fn default() -> Self {
240 Self {
241 name: "sphinx_rtd_theme".to_string(),
242 options: serde_json::json!({}),
243 custom_css: vec![],
244 custom_js: vec![],
245 }
246 }
247}
248
249impl Default for OptimizationConfig {
250 fn default() -> Self {
251 Self {
252 parallel_processing: true,
253 incremental_builds: true,
254 document_caching: true,
255 image_optimization: false,
256 asset_bundling: false,
257 }
258 }
259}
260
261impl BuildConfig {
262 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
263 let path = path.as_ref();
264 let content = std::fs::read_to_string(path)?;
265 let config = if path.extension().and_then(|s| s.to_str()) == Some("yaml")
266 || path.extension().and_then(|s| s.to_str()) == Some("yml")
267 {
268 serde_yaml::from_str(&content)?
269 } else {
270 serde_json::from_str(&content)?
271 };
272 Ok(config)
273 }
274
275 pub fn from_conf_py<P: AsRef<std::path::Path>>(conf_py_path: P) -> Result<Self> {
277 let mut parser = PythonConfigParser::new()?;
278 let conf_py_config = parser.parse_conf_py(conf_py_path)?;
279 Ok(conf_py_config.to_build_config())
280 }
281
282 pub fn auto_detect<P: AsRef<std::path::Path>>(source_dir: P) -> Result<Self> {
284 let source_dir = source_dir.as_ref();
285
286 let conf_py_path = source_dir.join("conf.py");
288 if conf_py_path.exists() {
289 return Self::from_conf_py(conf_py_path);
290 }
291
292 let yaml_path = source_dir.join("sphinx-ultra.yaml");
294 if yaml_path.exists() {
295 return Self::from_file(yaml_path);
296 }
297
298 let yml_path = source_dir.join("sphinx-ultra.yml");
300 if yml_path.exists() {
301 return Self::from_file(yml_path);
302 }
303
304 let json_path = source_dir.join("sphinx-ultra.json");
306 if json_path.exists() {
307 return Self::from_file(json_path);
308 }
309
310 Ok(Self::default())
312 }
313
314 #[allow(dead_code)]
315 pub fn save_to_file<P: AsRef<std::path::Path>>(&self, path: P) -> Result<()> {
316 let content = if path.as_ref().extension().and_then(|s| s.to_str()) == Some("yaml")
317 || path.as_ref().extension().and_then(|s| s.to_str()) == Some("yml")
318 {
319 serde_yaml::to_string(self)?
320 } else {
321 serde_json::to_string_pretty(self)?
322 };
323 std::fs::write(path, content)?;
324 Ok(())
325 }
326}