pub mod homebrew;
pub mod install;
pub mod path;
pub mod shell;
pub mod symlink;
use std::collections::HashMap;
use std::path::Path;
use serde::Serialize;
use crate::datastore::DataStore;
use crate::fs::Fs;
use crate::operations::HandlerIntent;
use crate::paths::Pather;
use crate::rules::RuleMatch;
use crate::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub enum HandlerCategory {
Configuration,
CodeExecution,
}
#[derive(Debug, Clone, Serialize)]
pub struct HandlerStatus {
pub file: String,
pub handler: String,
pub deployed: bool,
pub message: String,
}
pub trait Handler: Send + Sync {
fn name(&self) -> &str;
fn category(&self) -> HandlerCategory;
fn to_intents(
&self,
matches: &[RuleMatch],
config: &HandlerConfig,
paths: &dyn Pather,
) -> Result<Vec<HandlerIntent>>;
fn check_status(
&self,
file: &Path,
pack: &str,
datastore: &dyn DataStore,
) -> Result<HandlerStatus>;
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct HandlerConfig {
pub force_home: Vec<String>,
pub protected_paths: Vec<String>,
#[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")]
pub targets: std::collections::HashMap<String, String>,
}
pub const HANDLER_SYMLINK: &str = "symlink";
pub const HANDLER_SHELL: &str = "shell";
pub const HANDLER_PATH: &str = "path";
pub const HANDLER_INSTALL: &str = "install";
pub const HANDLER_HOMEBREW: &str = "homebrew";
pub fn create_registry(fs: &dyn Fs) -> HashMap<String, Box<dyn Handler + '_>> {
let mut registry: HashMap<String, Box<dyn Handler>> = HashMap::new();
registry.insert(HANDLER_SYMLINK.into(), Box::new(symlink::SymlinkHandler));
registry.insert(HANDLER_SHELL.into(), Box::new(shell::ShellHandler));
registry.insert(HANDLER_PATH.into(), Box::new(path::PathHandler));
registry.insert(
HANDLER_INSTALL.into(),
Box::new(install::InstallHandler::new(fs)),
);
registry.insert(
HANDLER_HOMEBREW.into(),
Box::new(homebrew::HomebrewHandler::new(fs)),
);
registry
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(dead_code)]
fn assert_object_safe(_: &dyn Handler) {}
#[allow(dead_code)]
fn assert_boxable(_: Box<dyn Handler>) {}
#[test]
fn handler_category_eq() {
assert_eq!(
HandlerCategory::Configuration,
HandlerCategory::Configuration
);
assert_ne!(
HandlerCategory::Configuration,
HandlerCategory::CodeExecution
);
}
#[test]
fn handler_status_serializes() {
let status = HandlerStatus {
file: "vimrc".into(),
handler: "symlink".into(),
deployed: true,
message: "linked to ~/.vimrc".into(),
};
let json = serde_json::to_string(&status).unwrap();
assert!(json.contains("deployed"));
assert!(json.contains("linked to ~/.vimrc"));
}
#[test]
fn handler_config_default() {
let config = HandlerConfig::default();
assert!(config.force_home.is_empty());
assert!(config.protected_paths.is_empty());
}
}