use crate::{CheckArgs, ClientType, detect_client, detect_installed_clients};
use std::path::PathBuf;
#[derive(Debug, Clone)]
pub enum ScanMode {
Paths(Vec<PathBuf>),
AllClients,
SingleClient(ClientType),
}
impl ScanMode {
pub fn from_check_args(args: &CheckArgs) -> Self {
if args.all_clients {
ScanMode::AllClients
} else if let Some(client) = args.client {
ScanMode::SingleClient(client)
} else {
ScanMode::Paths(args.paths.clone())
}
}
}
pub fn resolve_scan_paths_from_check_args(args: &CheckArgs) -> Vec<PathBuf> {
let mode = ScanMode::from_check_args(args);
resolve_scan_paths_for_mode(mode)
}
fn resolve_scan_paths_for_mode(mode: ScanMode) -> Vec<PathBuf> {
match mode {
ScanMode::Paths(paths) => {
if paths.is_empty() {
vec![PathBuf::from(".")]
} else {
paths
}
}
ScanMode::AllClients => {
let clients = detect_installed_clients();
if clients.is_empty() {
eprintln!("No AI coding clients detected on this system.");
return Vec::new();
}
let mut paths = Vec::new();
for client in &clients {
eprintln!(
"Detected {}: {}",
client.client_type.display_name(),
client.home_dir.display()
);
paths.extend(client.all_configs());
}
paths
}
ScanMode::SingleClient(client_type) => match detect_client(client_type) {
Some(client) => {
eprintln!(
"Scanning {}: {}",
client.client_type.display_name(),
client.home_dir.display()
);
client.all_configs()
}
None => {
eprintln!(
"{} is not installed or has no configuration files.",
client_type.display_name()
);
Vec::new()
}
},
}
}
pub fn detect_client_for_path(path: &str) -> Option<String> {
for client_type in ClientType::all() {
if let Some(home) = client_type.home_dir() {
let home_str = home.display().to_string();
if path.starts_with(&home_str) {
return Some(client_type.display_name().to_string());
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_check_args(paths: Vec<PathBuf>) -> CheckArgs {
CheckArgs {
paths,
..Default::default()
}
}
#[test]
fn test_scan_mode_from_check_args_paths() {
let args = CheckArgs {
paths: vec![PathBuf::from("/test/path")],
all_clients: false,
client: None,
..Default::default()
};
match ScanMode::from_check_args(&args) {
ScanMode::Paths(paths) => assert_eq!(paths, vec![PathBuf::from("/test/path")]),
_ => panic!("Expected ScanMode::Paths"),
}
}
#[test]
fn test_scan_mode_from_check_args_all_clients() {
let args = CheckArgs {
paths: vec![],
all_clients: true,
client: None,
..Default::default()
};
assert!(matches!(
ScanMode::from_check_args(&args),
ScanMode::AllClients
));
}
#[test]
fn test_scan_mode_from_check_args_single_client() {
let args = CheckArgs {
paths: vec![],
all_clients: false,
client: Some(ClientType::Claude),
..Default::default()
};
match ScanMode::from_check_args(&args) {
ScanMode::SingleClient(client) => assert_eq!(client, ClientType::Claude),
_ => panic!("Expected ScanMode::SingleClient"),
}
}
#[test]
fn test_scan_mode_debug() {
let mode = ScanMode::Paths(vec![PathBuf::from("./test")]);
let debug_str = format!("{:?}", mode);
assert!(debug_str.contains("Paths"));
let mode2 = ScanMode::AllClients;
let debug_str2 = format!("{:?}", mode2);
assert!(debug_str2.contains("AllClients"));
let mode3 = ScanMode::SingleClient(ClientType::Claude);
let debug_str3 = format!("{:?}", mode3);
assert!(debug_str3.contains("SingleClient"));
}
#[test]
fn test_resolve_scan_paths_empty() {
let args = create_test_check_args(vec![]);
let paths = resolve_scan_paths_from_check_args(&args);
assert_eq!(paths, vec![PathBuf::from(".")]);
}
#[test]
fn test_resolve_scan_paths_with_paths() {
let args = create_test_check_args(vec![
PathBuf::from("/test/path1"),
PathBuf::from("/test/path2"),
]);
let paths = resolve_scan_paths_from_check_args(&args);
assert_eq!(
paths,
vec![PathBuf::from("/test/path1"), PathBuf::from("/test/path2")]
);
}
#[test]
fn test_detect_client_for_path_unknown() {
let result = detect_client_for_path("/some/random/path");
let _ = result;
}
#[test]
fn test_scan_mode_clone() {
let mode = ScanMode::AllClients;
let cloned = mode.clone();
assert!(matches!(cloned, ScanMode::AllClients));
}
#[test]
fn test_resolve_scan_paths_all_clients() {
let args = CheckArgs {
paths: vec![],
all_clients: true,
client: None,
..Default::default()
};
let _paths = resolve_scan_paths_from_check_args(&args);
}
#[test]
fn test_resolve_scan_paths_single_client_claude() {
let args = CheckArgs {
paths: vec![],
all_clients: false,
client: Some(ClientType::Claude),
..Default::default()
};
let _paths = resolve_scan_paths_from_check_args(&args);
}
#[test]
fn test_resolve_scan_paths_single_client_cursor() {
let args = CheckArgs {
paths: vec![],
all_clients: false,
client: Some(ClientType::Cursor),
..Default::default()
};
let _paths = resolve_scan_paths_from_check_args(&args);
}
#[test]
fn test_resolve_scan_paths_single_client_windsurf() {
let args = CheckArgs {
paths: vec![],
all_clients: false,
client: Some(ClientType::Windsurf),
..Default::default()
};
let _paths = resolve_scan_paths_from_check_args(&args);
}
#[test]
fn test_resolve_scan_paths_single_client_vscode() {
let args = CheckArgs {
paths: vec![],
all_clients: false,
client: Some(ClientType::Vscode),
..Default::default()
};
let _paths = resolve_scan_paths_from_check_args(&args);
}
#[test]
fn test_detect_client_for_path_all_clients() {
for client_type in ClientType::all() {
if let Some(home) = client_type.home_dir() {
let test_path = format!("{}/test/file.json", home.display());
let result = detect_client_for_path(&test_path);
assert!(result.is_some() || result.is_none());
}
}
}
}