gitcore 1.4.0

A secure, zero-friction Git identity manager for developers who juggle multiple accounts.
Documentation
use crate::error::Result;
use crate::models::GitcoreConfig;
use std::fs;
use std::path::PathBuf;

pub fn default_config_path() -> PathBuf {
    dirs::config_dir()
        .unwrap_or_else(|| PathBuf::from("~/.config"))
        .join("gitcore")
        .join("config.json")
}

pub fn load_config_from_path(path: &PathBuf) -> Result<GitcoreConfig> {
    if !path.exists() {
        return Ok(GitcoreConfig::default());
    }

    let content = fs::read_to_string(path)?;
    let config = serde_json::from_str(&content)?;
    Ok(config)
}

pub fn save_config_to_path(config: &GitcoreConfig, path: &PathBuf) -> Result<()> {
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    let content = serde_json::to_string_pretty(config)?;
    let tmp_path = path.with_extension("tmp");
    fs::write(&tmp_path, content)?;

    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        fs::set_permissions(&tmp_path, fs::Permissions::from_mode(0o600))?;
    }

    fs::rename(&tmp_path, path)?;

    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        fs::set_permissions(path, fs::Permissions::from_mode(0o600))?;
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::models::{Account, Platform};
    use std::fs;
    use tempfile::tempdir;

    #[test]
    fn test_config_cycle() {
        let dir = tempdir().unwrap();
        let config_path = dir.path().join("config.json");

        let mut config = GitcoreConfig::default();
        config.accounts.push(Account {
            name: "test".to_string(),
            platform: Platform::Github,
            key_path: "key".to_string(),
            host_alias: "alias".to_string(),
            username: "user".to_string(),
            email: "email".to_string(),
            gpg_key_id: None,
        });

        save_config_to_path(&config, &config_path).unwrap();
        assert!(config_path.exists());

        let loaded = load_config_from_path(&config_path).unwrap();
        assert_eq!(loaded.accounts.len(), 1);
        assert_eq!(loaded.accounts[0].name, "test");

        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            let metadata = fs::metadata(&config_path).unwrap();
            assert_eq!(metadata.permissions().mode() & 0o777, 0o600);
        }
    }
}