use std::path::Path;
use serde::Deserialize;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(default)]
pub struct Features {
pub graph: bool,
pub math: bool,
pub mermaid: bool,
pub search: bool,
}
impl Default for Features {
fn default() -> Self {
Self {
graph: true,
math: true,
mermaid: true,
search: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(default)]
pub struct ComponentsConfig {
pub dir: String,
}
impl Default for ComponentsConfig {
fn default() -> Self {
Self {
dir: "components".to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Deserialize)]
#[serde(default)]
pub struct SiteConfig {
pub title: Option<String>,
pub base: String,
pub features: Features,
pub components: ComponentsConfig,
}
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
#[error("reading {path}: {source}")]
Io {
path: String,
#[source]
source: std::io::Error,
},
#[error("parsing {path}: {source}")]
Parse {
path: String,
#[source]
source: toml::de::Error,
},
}
pub fn load(project_root: &Path) -> Result<SiteConfig, ConfigError> {
let path = project_root.join("docgen.toml");
let text = match std::fs::read_to_string(&path) {
Ok(t) => t,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(SiteConfig::default()),
Err(e) => {
return Err(ConfigError::Io {
path: path.display().to_string(),
source: e,
})
}
};
toml::from_str(&text).map_err(|e| ConfigError::Parse {
path: path.display().to_string(),
source: e,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_pre_p6_behaviour() {
let c = SiteConfig::default();
assert_eq!(c.title, None);
assert_eq!(c.base, "");
assert!(c.features.graph && c.features.math && c.features.mermaid && c.features.search);
assert_eq!(c.components.dir, "components");
}
#[test]
fn missing_file_yields_default() {
let dir = tempfile::tempdir().unwrap();
assert_eq!(load(dir.path()).unwrap(), SiteConfig::default());
}
#[test]
fn parses_title_base_and_feature_toggles() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(
dir.path().join("docgen.toml"),
"title = \"My Docs\"\nbase = \"/docs\"\n[features]\ngraph = false\nmermaid = false\n",
)
.unwrap();
let c = load(dir.path()).unwrap();
assert_eq!(c.title.as_deref(), Some("My Docs"));
assert_eq!(c.base, "/docs");
assert!(!c.features.graph);
assert!(!c.features.mermaid);
assert!(c.features.math);
assert!(c.features.search);
}
#[test]
fn partial_features_table_keeps_other_defaults() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(
dir.path().join("docgen.toml"),
"[features]\nsearch = false\n",
)
.unwrap();
let c = load(dir.path()).unwrap();
assert!(!c.features.search);
assert!(c.features.graph);
}
#[test]
fn malformed_toml_is_an_error() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("docgen.toml"), "title = = =\n").unwrap();
assert!(load(dir.path()).is_err());
}
}