use crate::{config, schema};
use std::path::Path;
pub fn run(cmd_dir: &Path, typed: &[String]) -> i32 {
let skip = config::load().map(|c| c.on.skip).unwrap_or_default();
if !skipped(typed, &skip) && takes(cmd_dir, typed) { 0 } else { 1 }
}
pub fn skipped(typed: &[String], skip: &[String]) -> bool {
skip.iter().any(|s| {
let s: Vec<&str> = s.split_whitespace().collect();
!s.is_empty() && typed.len() >= s.len() && typed.iter().zip(&s).all(|(t, s)| t == s)
})
}
fn takes(cmd_dir: &Path, typed: &[String]) -> bool {
if typed.iter().any(|w| w.starts_with('-') || is_operator(w)) {
return false;
}
match schema::resolve(cmd_dir, typed) {
Ok((_, used)) => used < typed.len(),
Err(_) => false,
}
}
fn is_operator(w: &str) -> bool {
if w.starts_with("<(") || w.starts_with(">(") || w.starts_with("=(") {
return true;
}
let rest = w.trim_start_matches(|c: char| c.is_ascii_digit());
!rest.is_empty() && rest.chars().all(|c| "|&;<>()".contains(c))
}
#[cfg(test)]
mod tests {
use super::*;
fn words(line: &str) -> Vec<String> {
line.split_whitespace().map(String::from).collect()
}
fn examples() -> std::path::PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("examples")
}
#[test]
fn takes_words_for_a_known_command() {
for line in ["find log files older than 7 days", "find /var/log big files", "docker run nginx on port 8080", "curl https://example.com"] {
assert!(takes(&examples(), &words(line)), "{line}");
}
}
#[test]
fn leaves_the_rest_to_the_shell() {
for line in [
"",
"ls -la",
"git status",
"find",
"docker run",
"find . -name x",
"curl -s https://example.com",
"docker ps",
"find old logs | wc -l",
"find old logs && echo done",
"find old logs ; ls",
"find old logs > out.txt",
"find old logs 2> err.txt",
"find old logs &",
"diff <(find a) b",
] {
assert!(!takes(&examples(), &words(line)), "{line}");
}
}
#[test]
fn skip_list_matches_whole_words_from_the_start() {
let skip = vec!["kubectl".to_string(), "docker compose".to_string(), " ".to_string()];
for line in ["kubectl get pods", "docker compose up", "kubectl"] {
assert!(skipped(&words(line), &skip), "{line}");
}
for line in ["docker run nginx", "kubectlx get", "find empty folders", "docker", "pnpm kubectl"] {
assert!(!skipped(&words(line), &skip), "{line}");
}
}
#[test]
fn operators() {
for w in ["|", "||", "&&", ";", ">", ">>", "2>", "&>", "|&", "<(ls)"] {
assert!(is_operator(w), "{w}");
}
for w in ["7", "a|b", "logs", "*.log", "(x"] {
assert!(!is_operator(w), "{w}");
}
}
}