zenops 0.20.0

Declarative system configuration management for shell config and dotfiles.
use crate::{
    config::{Config, pkg::Shell, shell::bash::StoredBashConfig, shell::zsh::StoredZshConfig},
    config_files::ConfigFiles,
    error::Error,
};

mod bash;
mod common;
mod error;
mod zsh;

pub use error::Error as ConfigShellError;

/// Which interactive shell you use, and how zenops manages it.
///
/// Pick `type = "bash"` or `type = "zsh"` and zenops will derive an rc
/// file from your config — the environment variables, aliases, and
/// per-package init lines declared by your `[pkg.*]` entries. During
/// `apply` zenops compares the derived file against what's already on
/// disk and walks you through any differences hunk by hunk, so the file
/// only changes when you say so. You source the result from your real
/// `.bashrc` or `.zshrc` and the rest is hands-off.
///
/// Pick `type = "none"` to keep managing your shell by hand — zenops
/// won't derive or compare anything, but you also lose the per-package
/// shell init.
#[derive(serde::Deserialize, schemars::JsonSchema, Debug, Clone, PartialEq, Default)]
#[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)]
pub(super) enum StoredShellEnvironment {
    /// Don't manage any shell. zenops leaves your rc files alone. You
    /// also lose the per-package shell init that `bash` and `zsh` get.
    /// This is the default, so a fresh config doesn't quietly start
    /// editing your dotfiles.
    #[default]
    None,
    /// Manage bash. zenops derives `~/.zenops_bash_profile`; source it
    /// from your `~/.bash_profile` (on macOS) or `~/.bashrc` (on Linux).
    Bash(StoredBashConfig),
    /// Manage zsh. zenops derives `~/.zenops_zshrc`; source it from
    /// your `~/.zshrc`.
    Zsh(StoredZshConfig),
}

impl StoredShellEnvironment {
    pub fn update_config_files(
        &self,
        config: &Config,
        config_files: &mut ConfigFiles,
    ) -> Result<(), Error> {
        match self {
            Self::None => Ok(()),
            Self::Bash(shell_config) => bash::make_config_files(shell_config, config, config_files),
            Self::Zsh(shell_config) => zsh::make_config_files(shell_config, config, config_files),
        }
    }

    pub(super) fn shell(&self) -> Option<Shell> {
        match self {
            Self::None => None,
            Self::Bash(_) => Some(Shell::Bash),
            Self::Zsh(_) => Some(Shell::Zsh),
        }
    }
}