use serde::{Deserialize, Serialize};
use std::path::Path;
use crate::error::{Error, Result};
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct Config {
pub tables: TableConfig,
pub headings: HeadingConfig,
pub lists: ListConfig,
pub code: CodeConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct TableConfig {
pub align: bool,
pub min_column_width: usize,
pub padding: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct HeadingConfig {
pub blank_lines_before: usize,
pub blank_lines_after: usize,
pub space_after_hash: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ListConfig {
pub indent_size: usize,
pub marker: String,
pub normalize_numbers: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct CodeConfig {
pub ensure_language_tag: bool,
pub fence_style: String,
}
impl Default for TableConfig {
fn default() -> Self {
Self {
align: true,
min_column_width: 3,
padding: 1,
}
}
}
impl Default for HeadingConfig {
fn default() -> Self {
Self {
blank_lines_before: 2,
blank_lines_after: 1,
space_after_hash: true,
}
}
}
impl Default for ListConfig {
fn default() -> Self {
Self {
indent_size: 2,
marker: String::from("-"),
normalize_numbers: true,
}
}
}
impl Default for CodeConfig {
fn default() -> Self {
Self {
ensure_language_tag: false,
fence_style: String::from("```"),
}
}
}
impl Config {
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let content = std::fs::read_to_string(path.as_ref()).map_err(Error::Io)?;
let config: Self = toml::from_str(&content)?;
Ok(config)
}
#[must_use]
pub fn load_default() -> Self {
if let Ok(config) = Self::from_file(".beautiful-md.toml") {
return config;
}
if let Some(home) = dirs::home_dir() {
let home_config = home.join(".beautiful-md.toml");
if let Ok(config) = Self::from_file(home_config) {
return config;
}
}
Self::default()
}
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
let content = toml::to_string_pretty(self)
.map_err(|e| Error::ConfigError(format!("Failed to serialize config: {e}")))?;
std::fs::write(path.as_ref(), content)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = Config::default();
assert!(config.tables.align);
assert_eq!(config.tables.min_column_width, 3);
assert_eq!(config.headings.blank_lines_before, 2);
assert_eq!(config.lists.marker, "-");
}
#[test]
fn test_config_serialization() {
let config = Config::default();
let toml = toml::to_string(&config).unwrap();
let parsed: Config = toml::from_str(&toml).unwrap();
assert_eq!(parsed.tables.align, config.tables.align);
}
}