naru-config 0.7.0

A security-first configuration manager with encryption and audit logging
Documentation
use crate::core::persistence;
use crate::core::security;
use anyhow::{anyhow, Result};

pub struct DiffCommand {
    pub env1: String,
    pub env2: String,
}

impl DiffCommand {
    pub fn new(env1: String, env2: String) -> Self {
        DiffCommand { env1, env2 }
    }

    pub fn execute(&self) -> Result<()> {
        security::validate_environment_name(&self.env1)
            .map_err(|e| anyhow!("Invalid environment name: {}", e))?;
        security::validate_environment_name(&self.env2)
            .map_err(|e| anyhow!("Invalid environment name: {}", e))?;

        persistence::atomic_read_config(|config| {
            if !config.environments.contains_key(&self.env1) {
                return Err(anyhow!("Environment '{}' not found.", self.env1));
            }
            if !config.environments.contains_key(&self.env2) {
                return Err(anyhow!("Environment '{}' not found.", self.env2));
            }

            let env1_config = config
                .environments
                .get(&self.env1)
                .ok_or_else(|| anyhow!("Environment '{}' missing from config", self.env1))?;
            let env2_config = config
                .environments
                .get(&self.env2)
                .ok_or_else(|| anyhow!("Environment '{}' missing from config", self.env2))?;

            println!("\nDiff between '{}' and '{}':", self.env1, self.env2);
            println!("{}", "-".repeat(60));

            let mask_secret = |value: &str, is_secret: bool| -> String {
                if is_secret {
                    "********".to_string()
                } else {
                    value.to_string()
                }
            };

            let mut different_values = false;
            let mut only_in_env1 = false;
            let mut only_in_env2 = false;
            let mut same_values = false;

            for (key, entry1) in &env1_config.entries {
                if let Some(entry2) = env2_config.entries.get(key) {
                    if entry1.value != entry2.value {
                        let display_val1 = mask_secret(&entry1.value, entry1.is_secret);
                        let display_val2 = mask_secret(&entry2.value, entry2.is_secret);

                        if !entry1.is_secret && !entry2.is_secret {
                            println!(
                                "  ~ {}: {} -> {} ({} type)",
                                key, display_val1, display_val2, entry1.r#type
                            );
                        } else {
                            println!("  ~ {}: [modified] ({} type)", key, entry1.r#type);
                        }
                        different_values = true;
                    }
                }
            }

            for key in env1_config.entries.keys() {
                if !env2_config.entries.contains_key(key) {
                    let is_secret = env1_config
                        .entries
                        .get(key)
                        .map(|e| e.is_secret)
                        .unwrap_or(false);
                    if !is_secret {
                        println!("  - {} (only in {})", key, self.env1);
                    } else {
                        println!("  - {} (only in {}, secret)", key, self.env1);
                    }
                    only_in_env1 = true;
                }
            }

            for key in env2_config.entries.keys() {
                if !env1_config.entries.contains_key(key) {
                    let is_secret = env2_config
                        .entries
                        .get(key)
                        .map(|e| e.is_secret)
                        .unwrap_or(false);
                    if !is_secret {
                        println!("  + {} (only in {})", key, self.env2);
                    } else {
                        println!("  + {} (only in {}, secret)", key, self.env2);
                    }
                    only_in_env2 = true;
                }
            }

            for (key, entry1) in &env1_config.entries {
                if let Some(entry2) = env2_config.entries.get(key) {
                    if entry1.value == entry2.value {
                        if !entry1.is_secret {
                            println!("  = {}: {} ({})", key, entry1.value, entry1.r#type);
                        } else {
                            println!("  = {}: [secret] ({})", key, entry1.r#type);
                        }
                        same_values = true;
                    }
                }
            }

            if !different_values && !only_in_env1 && !only_in_env2 && !same_values {
                println!("  (none)");
            }

            Ok(())
        })
        .map_err(|e| anyhow!("Read error: {}", e))?
    }
}