sigmate 1.0.0

A modern, developer-focused CLI for cryptographic file signing and verification.
Documentation
use crate::error::AppError;
use serde::{Deserialize, Serialize};
use std::env;
use std::fs;
use std::path::PathBuf;

const APP_DIR_NAME: &str = "sigmate";
const CONFIG_FILE_NAME: &str = "config.json";
const KEYRING_DIR_NAME: &str = "public_keys";
const TRUST_STORE_FILE_NAME: &str = "trusted_public_keys.json";
pub const SIGNATURES_FOLDER: &str = "signatures";
pub const SIGMATE_META_FILE: &str = "sigmate.meta.json";
pub const SIGMATE_SBOM_FILE: &str = "sigmate.sbom.json";
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct AppConfig {
    pub private_key_path: Option<PathBuf>,
    pub signer_identity: Option<String>,
    pub keyring_path: Option<PathBuf>,
}

pub struct ConfigManager {
    config: AppConfig,
    config_path: PathBuf,
}

impl ConfigManager {
    pub fn new() -> Result<Self, AppError> {
        let config_dir = Self::get_config_dir()?;
        let config_path = config_dir.join(CONFIG_FILE_NAME);
        let mut manager = Self {
            config: AppConfig::default(),
            config_path,
        };
        manager.load()?;
        Ok(manager)
    }

    fn get_config_dir() -> Result<PathBuf, AppError> {
        dirs::config_dir()
            .map(|p| p.join(APP_DIR_NAME))
            .ok_or_else(|| AppError::Config("Could not determine config directory.".into()))
    }

    pub fn get_trust_store_path() -> Result<PathBuf, AppError> {
        Ok(Self::get_config_dir()?.join(TRUST_STORE_FILE_NAME))
    }

    pub fn get_default_keyring_path() -> Result<PathBuf, AppError> {
        Ok(Self::get_config_dir()?.join(KEYRING_DIR_NAME))
    }
    
    fn load(&mut self) -> Result<(), AppError> {
        if self.config_path.exists() {
            let content = fs::read_to_string(&self.config_path).map_err(|e| AppError::Io {
                path: self.config_path.clone(),
                source: e,
            })?;
            if !content.trim().is_empty() {
                 self.config = serde_json::from_str(&content)?;
            }
        }
        
        if self.config.keyring_path.is_none() {
            self.config.keyring_path = Some(Self::get_default_keyring_path()?);
        }

        Ok(())
    }

    pub fn save(&self) -> Result<(), AppError> {
        let config_dir = self.config_path.parent().ok_or_else(|| {
            AppError::Config("Invalid configuration file path.".to_string())
        })?;
        fs::create_dir_all(config_dir).map_err(|e| AppError::Io {
            path: config_dir.to_path_buf(),
            source: e,
        })?;
        let content = serde_json::to_string_pretty(&self.config)?;
        fs::write(&self.config_path, content).map_err(|e| AppError::Io {
            path: self.config_path.clone(),
            source: e,
        })
    }

    pub fn get_private_key_path(&self) -> Option<PathBuf> {
        env::var("SIGMATE_PRIVATE_KEY_PATH").map(PathBuf::from).ok().or_else(|| self.config.private_key_path.clone())
    }

    pub fn get_signer_identity(&self) -> Option<String> {
        env::var("SIGMATE_SIGNER_IDENTITY").ok().or_else(|| self.config.signer_identity.clone())
    }

    pub fn get_keyring_path(&self) -> Result<PathBuf, AppError> {
        let path = env::var("SIGMATE_KEYRING_PATH").map(PathBuf::from).ok().or(self.config.keyring_path.clone());
        
        match path {
            Some(p) => {
                 fs::create_dir_all(&p).map_err(|e| AppError::Io {
                    path: p.clone(),
                    source: e,
                })?;
                Ok(p)
            }
            None => Err(AppError::Config("Keyring path not configured and default could not be determined.".into()))
        }
    }
    
    pub fn set_private_key_path(&mut self, path: PathBuf) {
        self.config.private_key_path = Some(path);
    }

    pub fn set_signer_identity(&mut self, identity: String) {
        self.config.signer_identity = Some(identity);
    }
    
    pub fn set_keyring_path(&mut self, path: PathBuf) {
        self.config.keyring_path = Some(path);
    }

    pub fn get_config_path(&self) -> &PathBuf {
        &self.config_path
    }
}