1pub mod args;
2pub mod config;
3pub mod executor;
4
5use crate::args::LoopOptions;
6use std::path::PathBuf;
7use walkdir::WalkDir;
8
9pub fn run(args: LoopOptions) -> i32 {
15 if args.init {
16 config::create_looprc();
17 return exitcode::OK;
18 }
19
20 let options = LoopOptions {
21 command: args.command,
22 cwd: args.cwd,
23 include: args.include,
24 exclude: args.exclude,
25 include_only: args.include_only,
26 exclude_only: args.exclude_only,
27 include_pattern: args.include_pattern,
28 exclude_pattern: args.exclude_pattern,
29 init: args.init,
30 };
31
32 execute_loop(options)
33}
34
35pub fn execute_loop(options: LoopOptions) -> i32 {
40 let working_dir = options
41 .cwd
42 .clone()
43 .map(PathBuf::from)
44 .unwrap_or_else(|| std::env::current_dir().unwrap());
45 std::env::set_current_dir(working_dir).unwrap();
46
47 let config = config::read_looprc();
48
49 let mut first_error_code: Option<i32> = None;
50
51 for entry in WalkDir::new(".")
53 .min_depth(1)
54 .max_depth(1)
55 .sort_by_file_name()
56 {
57 let entry = entry.unwrap();
58 if entry.file_type().is_dir() {
59 let dir_path = entry.path();
60 if executor::should_process_directory(dir_path, &options, &config) {
61 let exit_code = executor::execute_command_in_directory(dir_path, &options.command);
62 if exit_code != 0 && first_error_code.is_none() {
63 first_error_code = Some(exit_code);
64 }
65 }
66 }
67 }
68
69 if let Some(ref include_dirs) = options.include {
71 for dir in include_dirs {
72 let dir_path = PathBuf::from(dir);
73 if dir_path.is_dir() {
74 let exit_code = executor::execute_command_in_directory(&dir_path, &options.command);
75 if exit_code != 0 && first_error_code.is_none() {
76 first_error_code = Some(exit_code);
77 }
78 } else {
79 eprintln!("Warning: {} is not a directory", dir);
80 }
81 }
82 }
83
84 match first_error_code {
85 Some(code) => code,
86 None => exitcode::OK,
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use std::fs;
94 use tempfile::tempdir;
95
96 #[test]
97 fn test_run() {
98 let temp_dir = tempdir().unwrap();
99 fs::create_dir(temp_dir.path().join("test_dir")).unwrap();
100
101 let args = LoopOptions {
102 command: vec!["echo".to_string(), "test".to_string()],
103 cwd: Some(temp_dir.path().to_string_lossy().into_owned()),
104 include: None,
105 exclude: None,
106 include_only: None,
107 exclude_only: None,
108 include_pattern: None,
109 exclude_pattern: None,
110 init: false,
111 };
112 let exit_code = run(args);
113 assert_eq!(exit_code, exitcode::OK);
114 }
115}