sdsconv 0.2.8

CLI for converting chemical safety SDS documents (PDF/DOCX) ↔ MHLW/JIS Z 7253 standard JSON via LLM (Claude/GPT/Gemini). Batch mode, multilingual.
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

#[derive(Serialize, Deserialize, Clone)]
#[serde(default)]
pub struct AppConfig {
    pub provider: String,
    pub api_key: String,
    pub model: String,
    pub language: String,
    pub quality: String,
    pub ui_lang: String,
    pub enrich: bool,
    pub base_url: String,
    /// Use the MHLW-recommended filename convention: SDS_日付_品番.json
    pub use_suggested_filename: bool,
}

impl Default for AppConfig {
    fn default() -> Self {
        Self {
            provider: "anthropic".into(),
            api_key: String::new(),
            model: String::new(),
            language: "ja".into(),
            quality: "medium".into(),
            ui_lang: "ja".into(),
            enrich: false,
            base_url: String::new(),
            use_suggested_filename: false,
        }
    }
}

impl AppConfig {
    fn config_path() -> Option<PathBuf> {
        dirs::config_dir().map(|d| d.join("sdsconv").join("config.toml"))
    }

    pub fn config_path_pub() -> Option<PathBuf> {
        Self::config_path()
    }

    pub fn load() -> Self {
        let Some(path) = Self::config_path() else {
            return Self::default();
        };
        let Ok(text) = std::fs::read_to_string(path) else {
            return Self::default();
        };
        toml::from_str(&text).unwrap_or_default()
    }

    pub fn save(&self) -> anyhow::Result<()> {
        let path = Self::config_path()
            .ok_or_else(|| anyhow::anyhow!("Cannot determine config directory"))?;
        if let Some(parent) = path.parent() {
            std::fs::create_dir_all(parent)?;
        }
        std::fs::write(&path, toml::to_string_pretty(self)?)?;
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600))?;
        }
        Ok(())
    }
}