1use crate::{CheckArgs, ClientType, detect_client, detect_installed_clients};
4use std::path::PathBuf;
5
6#[derive(Debug, Clone)]
8pub enum ScanMode {
9 Paths(Vec<PathBuf>),
11 AllClients,
13 SingleClient(ClientType),
15}
16
17impl ScanMode {
18 pub fn from_check_args(args: &CheckArgs) -> Self {
20 if args.all_clients {
21 ScanMode::AllClients
22 } else if let Some(client) = args.client {
23 ScanMode::SingleClient(client)
24 } else {
25 ScanMode::Paths(args.paths.clone())
26 }
27 }
28}
29
30pub fn resolve_scan_paths_from_check_args(args: &CheckArgs) -> Vec<PathBuf> {
32 let mode = ScanMode::from_check_args(args);
33 resolve_scan_paths_for_mode(mode)
34}
35
36fn resolve_scan_paths_for_mode(mode: ScanMode) -> Vec<PathBuf> {
38 match mode {
39 ScanMode::Paths(paths) => {
40 if paths.is_empty() {
41 vec![PathBuf::from(".")]
43 } else {
44 paths
45 }
46 }
47 ScanMode::AllClients => {
48 let clients = detect_installed_clients();
49 if clients.is_empty() {
50 eprintln!("No AI coding clients detected on this system.");
51 return Vec::new();
52 }
53
54 let mut paths = Vec::new();
55 for client in &clients {
56 eprintln!(
57 "Detected {}: {}",
58 client.client_type.display_name(),
59 client.home_dir.display()
60 );
61 paths.extend(client.all_configs());
62 }
63 paths
64 }
65 ScanMode::SingleClient(client_type) => match detect_client(client_type) {
66 Some(client) => {
67 eprintln!(
68 "Scanning {}: {}",
69 client.client_type.display_name(),
70 client.home_dir.display()
71 );
72 client.all_configs()
73 }
74 None => {
75 eprintln!(
76 "{} is not installed or has no configuration files.",
77 client_type.display_name()
78 );
79 Vec::new()
80 }
81 },
82 }
83}
84
85pub fn detect_client_for_path(path: &str) -> Option<String> {
87 for client_type in ClientType::all() {
88 if let Some(home) = client_type.home_dir() {
89 let home_str = home.display().to_string();
90 if path.starts_with(&home_str) {
91 return Some(client_type.display_name().to_string());
92 }
93 }
94 }
95 None
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 fn create_test_check_args(paths: Vec<PathBuf>) -> CheckArgs {
103 CheckArgs {
104 paths,
105 ..Default::default()
106 }
107 }
108
109 #[test]
110 fn test_scan_mode_from_check_args_paths() {
111 let args = CheckArgs {
112 paths: vec![PathBuf::from("/test/path")],
113 all_clients: false,
114 client: None,
115 ..Default::default()
116 };
117 match ScanMode::from_check_args(&args) {
118 ScanMode::Paths(paths) => assert_eq!(paths, vec![PathBuf::from("/test/path")]),
119 _ => panic!("Expected ScanMode::Paths"),
120 }
121 }
122
123 #[test]
124 fn test_scan_mode_from_check_args_all_clients() {
125 let args = CheckArgs {
126 paths: vec![],
127 all_clients: true,
128 client: None,
129 ..Default::default()
130 };
131 assert!(matches!(
132 ScanMode::from_check_args(&args),
133 ScanMode::AllClients
134 ));
135 }
136
137 #[test]
138 fn test_scan_mode_from_check_args_single_client() {
139 let args = CheckArgs {
140 paths: vec![],
141 all_clients: false,
142 client: Some(ClientType::Claude),
143 ..Default::default()
144 };
145 match ScanMode::from_check_args(&args) {
146 ScanMode::SingleClient(client) => assert_eq!(client, ClientType::Claude),
147 _ => panic!("Expected ScanMode::SingleClient"),
148 }
149 }
150
151 #[test]
152 fn test_scan_mode_debug() {
153 let mode = ScanMode::Paths(vec![PathBuf::from("./test")]);
154 let debug_str = format!("{:?}", mode);
155 assert!(debug_str.contains("Paths"));
156
157 let mode2 = ScanMode::AllClients;
158 let debug_str2 = format!("{:?}", mode2);
159 assert!(debug_str2.contains("AllClients"));
160
161 let mode3 = ScanMode::SingleClient(ClientType::Claude);
162 let debug_str3 = format!("{:?}", mode3);
163 assert!(debug_str3.contains("SingleClient"));
164 }
165
166 #[test]
167 fn test_resolve_scan_paths_empty() {
168 let args = create_test_check_args(vec![]);
169 let paths = resolve_scan_paths_from_check_args(&args);
170 assert_eq!(paths, vec![PathBuf::from(".")]);
171 }
172
173 #[test]
174 fn test_resolve_scan_paths_with_paths() {
175 let args = create_test_check_args(vec![
176 PathBuf::from("/test/path1"),
177 PathBuf::from("/test/path2"),
178 ]);
179 let paths = resolve_scan_paths_from_check_args(&args);
180 assert_eq!(
181 paths,
182 vec![PathBuf::from("/test/path1"), PathBuf::from("/test/path2")]
183 );
184 }
185
186 #[test]
187 fn test_detect_client_for_path_unknown() {
188 let result = detect_client_for_path("/some/random/path");
189 let _ = result;
192 }
193
194 #[test]
195 fn test_scan_mode_clone() {
196 let mode = ScanMode::AllClients;
197 let cloned = mode.clone();
198 assert!(matches!(cloned, ScanMode::AllClients));
199 }
200
201 #[test]
202 fn test_resolve_scan_paths_all_clients() {
203 let args = CheckArgs {
204 paths: vec![],
205 all_clients: true,
206 client: None,
207 ..Default::default()
208 };
209 let _paths = resolve_scan_paths_from_check_args(&args);
212 }
213
214 #[test]
215 fn test_resolve_scan_paths_single_client_claude() {
216 let args = CheckArgs {
217 paths: vec![],
218 all_clients: false,
219 client: Some(ClientType::Claude),
220 ..Default::default()
221 };
222 let _paths = resolve_scan_paths_from_check_args(&args);
225 }
226
227 #[test]
228 fn test_resolve_scan_paths_single_client_cursor() {
229 let args = CheckArgs {
230 paths: vec![],
231 all_clients: false,
232 client: Some(ClientType::Cursor),
233 ..Default::default()
234 };
235 let _paths = resolve_scan_paths_from_check_args(&args);
236 }
237
238 #[test]
239 fn test_resolve_scan_paths_single_client_windsurf() {
240 let args = CheckArgs {
241 paths: vec![],
242 all_clients: false,
243 client: Some(ClientType::Windsurf),
244 ..Default::default()
245 };
246 let _paths = resolve_scan_paths_from_check_args(&args);
247 }
248
249 #[test]
250 fn test_resolve_scan_paths_single_client_vscode() {
251 let args = CheckArgs {
252 paths: vec![],
253 all_clients: false,
254 client: Some(ClientType::Vscode),
255 ..Default::default()
256 };
257 let _paths = resolve_scan_paths_from_check_args(&args);
258 }
259
260 #[test]
261 fn test_detect_client_for_path_all_clients() {
262 for client_type in ClientType::all() {
264 if let Some(home) = client_type.home_dir() {
265 let test_path = format!("{}/test/file.json", home.display());
266 let result = detect_client_for_path(&test_path);
267 assert!(result.is_some() || result.is_none());
269 }
270 }
271 }
272}