use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use crate::python_config::PythonConfigParser;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BuildConfig {
pub parallel_jobs: Option<usize>,
pub max_cache_size_mb: usize,
pub cache_expiration_hours: u64,
pub output: OutputConfig,
pub theme: ThemeConfig,
pub extensions: Vec<String>,
pub template_dirs: Vec<PathBuf>,
pub static_dirs: Vec<PathBuf>,
pub optimization: OptimizationConfig,
pub project: String,
pub version: Option<String>,
pub release: Option<String>,
pub copyright: Option<String>,
pub language: Option<String>,
pub root_doc: Option<String>,
pub html_style: Vec<String>,
pub html_css_files: Vec<String>,
pub html_js_files: Vec<String>,
pub html_static_path: Vec<PathBuf>,
pub html_logo: Option<String>,
pub html_favicon: Option<String>,
pub html_title: Option<String>,
pub html_short_title: Option<String>,
pub html_show_copyright: Option<bool>,
pub html_show_sphinx: Option<bool>,
pub html_copy_source: Option<bool>,
pub html_show_sourcelink: Option<bool>,
pub html_sourcelink_suffix: Option<String>,
pub html_use_index: Option<bool>,
pub html_use_opensearch: Option<bool>,
pub html_last_updated_fmt: Option<String>,
pub templates_path: Vec<PathBuf>,
pub fail_on_warning: bool,
pub include_patterns: Vec<String>,
pub exclude_patterns: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutputConfig {
pub html_theme: String,
pub syntax_highlighting: bool,
pub highlight_theme: String,
pub search_index: bool,
pub minify_html: bool,
pub compress_output: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThemeConfig {
pub name: String,
pub options: serde_json::Value,
pub custom_css: Vec<PathBuf>,
pub custom_js: Vec<PathBuf>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OptimizationConfig {
pub parallel_processing: bool,
pub incremental_builds: bool,
pub document_caching: bool,
pub image_optimization: bool,
pub asset_bundling: bool,
}
impl Default for BuildConfig {
fn default() -> Self {
Self {
parallel_jobs: None,
max_cache_size_mb: 500,
cache_expiration_hours: 24,
output: OutputConfig::default(),
theme: ThemeConfig::default(),
extensions: vec![
"sphinx.ext.autodoc".to_string(),
"sphinx.ext.viewcode".to_string(),
"sphinx.ext.intersphinx".to_string(),
],
template_dirs: vec![],
static_dirs: vec![],
optimization: OptimizationConfig::default(),
project: "Sphinx Ultra Project".to_string(),
version: Some("1.0.0".to_string()),
release: Some("1.0.0".to_string()),
copyright: Some("2024, Sphinx Ultra".to_string()),
language: Some("en".to_string()),
root_doc: Some("index".to_string()),
html_style: vec!["sphinx_rtd_theme.css".to_string()],
html_css_files: vec![],
html_js_files: vec![],
html_static_path: vec![PathBuf::from("_static")],
html_logo: None,
html_favicon: None,
html_title: None,
html_short_title: None,
html_show_copyright: Some(true),
html_show_sphinx: Some(true),
html_copy_source: Some(true),
html_show_sourcelink: Some(true),
html_sourcelink_suffix: Some(".txt".to_string()),
html_use_index: Some(true),
html_use_opensearch: Some(false),
html_last_updated_fmt: Some("%b %d, %Y".to_string()),
templates_path: vec![PathBuf::from("_templates")],
fail_on_warning: false,
include_patterns: vec!["**".to_string()],
exclude_patterns: vec![],
}
}
}
impl Default for OutputConfig {
fn default() -> Self {
Self {
html_theme: "sphinx_rtd_theme".to_string(),
syntax_highlighting: true,
highlight_theme: "github".to_string(),
search_index: true,
minify_html: false,
compress_output: false,
}
}
}
impl Default for ThemeConfig {
fn default() -> Self {
Self {
name: "sphinx_rtd_theme".to_string(),
options: serde_json::json!({}),
custom_css: vec![],
custom_js: vec![],
}
}
}
impl Default for OptimizationConfig {
fn default() -> Self {
Self {
parallel_processing: true,
incremental_builds: true,
document_caching: true,
image_optimization: false,
asset_bundling: false,
}
}
}
impl BuildConfig {
pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
let path = path.as_ref();
let content = std::fs::read_to_string(path)?;
let config = if path.extension().and_then(|s| s.to_str()) == Some("yaml")
|| path.extension().and_then(|s| s.to_str()) == Some("yml")
{
serde_yaml::from_str(&content)?
} else {
serde_json::from_str(&content)?
};
Ok(config)
}
pub fn from_conf_py<P: AsRef<std::path::Path>>(conf_py_path: P) -> Result<Self> {
let mut parser = PythonConfigParser::new()?;
let conf_py_config = parser.parse_conf_py(conf_py_path)?;
Ok(conf_py_config.to_build_config())
}
pub fn auto_detect<P: AsRef<std::path::Path>>(source_dir: P) -> Result<Self> {
let source_dir = source_dir.as_ref();
let conf_py_path = source_dir.join("conf.py");
if conf_py_path.exists() {
return Self::from_conf_py(conf_py_path);
}
let yaml_path = source_dir.join("sphinx-ultra.yaml");
if yaml_path.exists() {
return Self::from_file(yaml_path);
}
let yml_path = source_dir.join("sphinx-ultra.yml");
if yml_path.exists() {
return Self::from_file(yml_path);
}
let json_path = source_dir.join("sphinx-ultra.json");
if json_path.exists() {
return Self::from_file(json_path);
}
Ok(Self::default())
}
#[allow(dead_code)]
pub fn save_to_file<P: AsRef<std::path::Path>>(&self, path: P) -> Result<()> {
let content = if path.as_ref().extension().and_then(|s| s.to_str()) == Some("yaml")
|| path.as_ref().extension().and_then(|s| s.to_str()) == Some("yml")
{
serde_yaml::to_string(self)?
} else {
serde_json::to_string_pretty(self)?
};
std::fs::write(path, content)?;
Ok(())
}
}