diff options
| author | 2026-02-27 00:41:20 -0500 | |
|---|---|---|
| committer | 2026-02-27 00:41:20 -0500 | |
| commit | f19dd10caff1d6393bffb11189c7467e268a33df (patch) | |
| tree | 06e4d9c9b7aeb7771aa0ad189816851094cee54d /src | |
| parent | add version flag (diff) | |
add the clean subcommand
Diffstat (limited to 'src')
| -rw-r--r-- | src/action.rs | 70 | ||||
| -rw-r--r-- | src/config.rs | 30 |
2 files changed, 88 insertions, 12 deletions
diff --git a/src/action.rs b/src/action.rs index fe405a3..a9c2563 100644 --- a/src/action.rs +++ b/src/action.rs @@ -2,7 +2,7 @@ use crate::config::{self, ConfigCommand, TEMP_CONFIG_PATH, create_config}; use crate::util::{dir_size, get_editor, open_in_editor, yn_prompt}; use git2::Repository; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; const BASE_REPO_PATH: &str = "/var/db/forge"; const BASE_CONFIG_PATH: &str = "/etc/forge/packages"; @@ -71,7 +71,7 @@ impl Action { Action::Remove { packages } => remove(packages), Action::List => list(), Action::Search { term } => Ok(search(term)), - Action::Clean { packages } => Ok(clean(packages)), + Action::Clean { packages } => clean(packages), Action::Show { package } => Ok(show(package)), Action::Version => Ok(version()), } @@ -192,8 +192,7 @@ fn remove(packages: Vec<String>) -> Result<(), String> { } fn list() -> Result<(), String> { - let config_path = Path::new(BASE_CONFIG_PATH); - for entry in fs::read_dir(config_path) + for entry in fs::read_dir(BASE_CONFIG_PATH) .map_err(|e| format!("failed to iterate package directory: {}", e))? { let entry = entry.map_err(|e| e.to_string())?; @@ -211,10 +210,67 @@ fn search(term: String) { println!("searching: {}", term); } -fn clean(packages: Vec<String>) { - for (_, p) in packages.iter().enumerate() { - println!("cleaning: {}", p); +fn clean(packages: Vec<String>) -> Result<(), String> { + if !nix::unistd::geteuid().is_root() { + return Err("clean must be run as root".to_string()); + } + + let package_paths: Vec<(String, PathBuf, PathBuf)> = if packages.is_empty() { + fs::read_dir(BASE_CONFIG_PATH) + .map_err(|e| format!("failed to iterate package directory: {}", e))? + .map(|p| { + let entry = p.map_err(|e| e.to_string())?; + let path = entry.path(); + + let pkgname = path + .file_stem() + .ok_or_else(|| format!("invalid filename: {:?}", path))? + .to_string_lossy() + .into_owned(); + + let path = PathBuf::from(BASE_REPO_PATH).join(&pkgname); + let cfg_path = PathBuf::from(BASE_CONFIG_PATH).join(format!("{}.toml", &pkgname)); + + if !path.exists() || !cfg_path.exists() { + Err(format!("no installed package: {}", pkgname)) + } else { + Ok((pkgname, path, cfg_path)) + } + }) + .collect::<Result<_, _>>()? + } else { + packages + .into_iter() + .map(|p| { + let path = PathBuf::from(BASE_REPO_PATH).join(&p); + let cfg_path = PathBuf::from(BASE_CONFIG_PATH).join(format!("{}.toml", p)); + if !path.exists() || !cfg_path.exists() { + Err(format!("no installed package: {}", p)) + } else { + Ok((p, path, cfg_path)) + } + }) + .collect::<Result<_, _>>()? + }; + + println!( + "Packages to clean ({}): {}\n", + package_paths.len(), + package_paths + .iter() + .map(|(p, _, _)| p.as_str()) + .collect::<Vec<_>>() + .join(", ") + ); + + if yn_prompt("Proceed with cleanup?") { + for (name, path, cfg_path) in package_paths { + config::run_config_command(&cfg_path, &path, ConfigCommand::Clean)?; + println!("Cleaned {}", name); + } } + + Ok(()) } fn show(package: String) { diff --git a/src/config.rs b/src/config.rs index ecada2a..8e3437c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,15 +9,26 @@ pub enum ConfigCommand { Build, Install, Uninstall, + PostInstall, + PostUninstall, + Clean, } #[derive(Deserialize)] pub struct Config { pub update: Option<String>, + pub dependencies: Option<Vec<String>>, + pub hooks: Option<Hooks>, +} + +#[derive(Deserialize)] +pub struct Hooks { pub build: Option<String>, pub install: Option<String>, pub uninstall: Option<String>, - pub dependencies: Option<Vec<String>>, + pub post_install: Option<String>, + pub post_uninstall: Option<String>, + pub clean: Option<String>, } impl Config { @@ -48,10 +59,15 @@ pub fn create_config(package: &str) -> Result<(), String> { let template = format!( r#"# {package} configuration update = "tagged" # no | live | tagged +dependencies = [] + +[hooks] build = "make" install = "make install" uninstall = "make uninstall" -dependencies = [] +post_install = "" +post_uninstall = "" +clean = "make clean" "# ); @@ -66,11 +82,15 @@ pub fn run_config_command( command: ConfigCommand, ) -> Result<(), String> { let config = Config::new(config_path).ok_or("config not found".to_string())?; + let hooks = config.hooks.ok_or("no hooks section".to_string())?; let cmd = match command { - ConfigCommand::Build => config.build, - ConfigCommand::Install => config.install, - ConfigCommand::Uninstall => config.uninstall, + ConfigCommand::Build => hooks.build, + ConfigCommand::Install => hooks.install, + ConfigCommand::Uninstall => hooks.uninstall, + ConfigCommand::PostInstall => hooks.post_install, + ConfigCommand::PostUninstall => hooks.post_uninstall, + ConfigCommand::Clean => hooks.clean, }; if let Some(c) = cmd { |
