add purge command
This commit is contained in:
parent
4a7febee6c
commit
2682ed958b
6 changed files with 97 additions and 4 deletions
|
|
@ -92,8 +92,8 @@ inshellah dump
|
|||
# view stored data for a command
|
||||
inshellah query docker
|
||||
|
||||
# clear cache
|
||||
rm -rf ~/.cache/inshellah/
|
||||
# clear the on-the-fly user cache (.json/.nu files; system dirs untouched)
|
||||
inshellah purge
|
||||
|
||||
# re-index from a prefix
|
||||
inshellah index /usr --dir ~/.cache/inshellah
|
||||
|
|
|
|||
|
|
@ -241,7 +241,7 @@ in
|
|||
dirPaths = lib.concatStringsSep ":" ([ systemDir ] ++ cfg.extraDirs);
|
||||
wrapped = pkgs.writeShellScriptBin "inshellah" ''
|
||||
case "''${1:-}" in
|
||||
complete|query|dump)
|
||||
complete|query|dump|purge)
|
||||
exec ${cfg.package}/bin/inshellah "$@" --dir "''${XDG_CACHE_HOME:-$HOME/.cache}/inshellah:${dirPaths}"
|
||||
;;
|
||||
*)
|
||||
|
|
|
|||
26
src/main.rs
26
src/main.rs
|
|
@ -31,7 +31,7 @@ use inshellah::parsers::nushell::{generate_extern, generate_module, is_nushell_b
|
|||
use inshellah::pool::{ScrapePool, Submitter};
|
||||
use inshellah::store::{
|
||||
all_commands, default_store_path, ensure_dir, file_type_of, filename_of_command, lookup,
|
||||
lookup_raw, parse_nu_completions, subcommands_of, write_native, write_result,
|
||||
lookup_raw, parse_nu_completions, purge_dir, subcommands_of, write_native, write_result,
|
||||
};
|
||||
|
||||
const COMMAND_SECTIONS: &[u8] = &[1, 8];
|
||||
|
|
@ -61,6 +61,10 @@ Usage:
|
|||
Print stored completion data for CMD.
|
||||
inshellah dump [--dir PATH[:PATH...]]
|
||||
List indexed commands.
|
||||
inshellah purge [--dir PATH[:PATH...]]
|
||||
Delete the on-the-fly user cache (.json/.nu files). Only the first
|
||||
--dir (the writable user cache) is cleared; system dirs are untouched.
|
||||
Default dir: $XDG_CACHE_HOME/inshellah
|
||||
inshellah manpage FILE Parse a manpage and emit nushell extern
|
||||
inshellah manpage-dir DIR Batch-process manpages under DIR
|
||||
inshellah completions Generate nushell completions for inshellah
|
||||
|
|
@ -1291,6 +1295,18 @@ fn cmd_dump(dirs: &[PathBuf]) {
|
|||
}
|
||||
}
|
||||
|
||||
/// purge the on-the-fly user cache. only the writable user dir is cleared;
|
||||
/// read-only system overlays are never touched.
|
||||
fn cmd_purge(user_dir: &Path) {
|
||||
match purge_dir(user_dir) {
|
||||
Ok(n) => println!("purged {n} cached entries from {}", user_dir.display()),
|
||||
Err(e) => {
|
||||
eprintln!("purge failed: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// look up a command's path in $PATH.
|
||||
fn find_in_path(name: &str) -> Option<PathBuf> {
|
||||
let path_var = std::env::var("PATH").ok()?;
|
||||
|
|
@ -2185,6 +2201,7 @@ fn cmd_completions() {
|
|||
"complete",
|
||||
"query",
|
||||
"dump",
|
||||
"purge",
|
||||
"completions",
|
||||
];
|
||||
let mut subcommands = Vec::new();
|
||||
|
|
@ -2442,6 +2459,13 @@ fn main() {
|
|||
let (_, dirs, _timeout_ms) = parse_dir_args(&args[2..]);
|
||||
cmd_dump(&dirs);
|
||||
}
|
||||
"purge" => {
|
||||
let (_, dirs, _timeout_ms) = parse_dir_args(&args[2..]);
|
||||
// only the first (writable user) dir is purged; the rest are
|
||||
// read-only system overlays we must never delete from.
|
||||
let user_dir = dirs.first().cloned().unwrap_or_else(default_store_path);
|
||||
cmd_purge(&user_dir);
|
||||
}
|
||||
"completions" => cmd_completions(),
|
||||
"--help" | "-h" | "help" => usage(),
|
||||
other => {
|
||||
|
|
|
|||
27
src/store.rs
27
src/store.rs
|
|
@ -579,6 +579,33 @@ pub fn all_commands(dirs: &[PathBuf]) -> Vec<String> {
|
|||
out.into_iter().collect()
|
||||
}
|
||||
|
||||
/// remove every inshellah cache file (`.json` / `.nu`) from a single store
|
||||
/// directory. only those extensions are touched, so even a misaimed dir
|
||||
/// won't wipe unrelated files, and the directory itself is left in place.
|
||||
/// a missing directory is treated as already empty. returns how many files
|
||||
/// were removed.
|
||||
pub fn purge_dir(dir: &Path) -> io::Result<usize> {
|
||||
let entries = match fs::read_dir(dir) {
|
||||
Ok(entries) => entries,
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(0),
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
let mut removed = 0;
|
||||
for entry in entries.flatten() {
|
||||
let path = entry.path();
|
||||
let is_cache_file = path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.and_then(chop_extension)
|
||||
.is_some();
|
||||
if is_cache_file && path.is_file() {
|
||||
fs::remove_file(&path)?;
|
||||
removed += 1;
|
||||
}
|
||||
}
|
||||
Ok(removed)
|
||||
}
|
||||
|
||||
/// discover subcommands of a command by scanning filenames in the store
|
||||
/// (e.g. for "git", finds "git_add.json", "git_log.json").
|
||||
pub fn subcommands_of(dirs: &[PathBuf], command: &str) -> Vec<ManpageSubcommand> {
|
||||
|
|
|
|||
|
|
@ -665,6 +665,47 @@ fn flag_demo_cache(name: &str, flags: &[&str]) -> std::path::PathBuf {
|
|||
cache_dir
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn purge_clears_user_cache_but_not_system_dirs() {
|
||||
let root = unique_temp_dir("inshellah-purge");
|
||||
let user_dir = root.join("cache");
|
||||
let system_dir = root.join("system");
|
||||
fs::create_dir_all(&user_dir).expect("user dir");
|
||||
fs::create_dir_all(&system_dir).expect("system dir");
|
||||
|
||||
let result = ManpageResult {
|
||||
entries: Vec::new(),
|
||||
subcommands: Vec::new(),
|
||||
positionals: Vec::new(),
|
||||
description: String::new(),
|
||||
};
|
||||
write_result(&user_dir, "usercmd", "help", &result).expect("user cache");
|
||||
write_result(&system_dir, "syscmd", "manpage", &result).expect("system cache");
|
||||
// a non-cache file in the user dir must survive the purge.
|
||||
fs::write(user_dir.join("keep.txt"), "keep me").expect("sentinel");
|
||||
|
||||
let dir_arg = format!("{}:{}", user_dir.display(), system_dir.display());
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_inshellah"))
|
||||
.args(["purge", "--dir", &dir_arg])
|
||||
.output()
|
||||
.expect("run inshellah purge");
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"stderr = {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
|
||||
// user cache entry gone, non-cache file kept, system dir untouched.
|
||||
assert!(!user_dir.join("usercmd.json").exists(), "user entry not purged");
|
||||
assert!(user_dir.join("keep.txt").exists(), "non-cache file removed");
|
||||
assert!(
|
||||
system_dir.join("syscmd.json").exists(),
|
||||
"system dir must not be purged"
|
||||
);
|
||||
|
||||
let _ = fs::remove_dir_all(root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complete_flag_on_empty_env_surfaces_flags_after_space() {
|
||||
let cache_dir = flag_demo_cache("inshellah-flag-on-empty", &["verbose"]);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ fn inshellah_completions_include_all_subcommands() {
|
|||
"complete",
|
||||
"query",
|
||||
"dump",
|
||||
"purge",
|
||||
"completions",
|
||||
] {
|
||||
let extern_name = format!("export extern \"inshellah {subcommand}\"");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue