1#![allow(clippy::missing_errors_doc)]
4#![allow(clippy::missing_panics_doc)]
5
6use crate::core::backend::{
7 search_for_repos,
8 exec_async_check
9};
10use crate::utils::{
11 APP_NAME,
12 TrackingFile,
13 repo_is_tracked,
14 repos_valid
15};
16
17use std::fs::{self, OpenOptions};
18use std::io::Write as _;
19use std::path::Path;
20
21pub fn scan_dirs(mut dirs: Vec<String>, tracking_file: &TrackingFile, scan_hidden: bool) -> Result<(), String> {
23 dirs.sort_unstable();
25 dirs.dedup();
26
27 let mut dirs_ok = true;
30
31 for dir in &mut dirs {
32 let path = Path::new(&dir);
33
34 if let Ok(p) = path.try_exists() {
36 if !p {
37 eprintln!("{APP_NAME}: Directory '{dir}' does not exist");
38 dirs_ok = false;
39 continue;
40 }
41 }
42 else {
43 eprintln!("{APP_NAME}: Cannot check the existance of directory '{dir}'");
44 dirs_ok = false;
45 continue;
46 }
47
48 if path.is_file() {
50 eprintln!("{APP_NAME}: '{dir}' is not a directory");
51 dirs_ok = false;
52 }
53
54 if let Some(s) = fs::canonicalize(&dir)
57 .map_err(|e| format!("{dir}: {e}"))?
58 .to_str() {
59 *dir = s.to_string();
60 }
61 else {
62 eprintln!("{APP_NAME}: {dir}: The path contains invalid UTF-8 characters");
63 dirs_ok = false;
64 }
65 }
66
67 if !dirs_ok {
68 return Err(String::from("Directories validation failed"));
69 }
70
71 search_for_repos(dirs.as_slice(), tracking_file, scan_hidden)?;
72
73 Ok(())
74}
75
76pub fn scan_all(home_dir: String, tracking_file: &TrackingFile, scan_hidden: bool) -> Result<(), String> {
78 search_for_repos(&[home_dir], tracking_file, scan_hidden)?;
79
80 Ok(())
81}
82
83pub fn list(track_file_contents: &str) -> Result<(), String> {
85 if track_file_contents.is_empty() {
86 return Err(String::from("No repository is being tracked"));
87 }
88
89 print!("{track_file_contents}");
90
91 Ok(())
92}
93
94pub fn add(mut repos: Vec<String>, tracking_file: &TrackingFile) -> Result<(), String> {
96 repos.sort_unstable();
98 repos.dedup();
99
100 repos = repos_valid(repos.as_slice())?;
101
102 let mut track_file = OpenOptions::new()
104 .create(true)
105 .append(true)
106 .open(tracking_file.path.clone())
107 .map_err(|e| format!("{}: {e}", tracking_file.path))?;
108
109 for repo in repos {
110 if repo_is_tracked(repo.as_str(), tracking_file.contents.as_str()) {
113 println!("{APP_NAME}: '{repo}' is already being tracked");
114 continue;
115 }
116
117 track_file.write_all(
119 format!("{repo}\n").as_bytes())
120 .map_err(|e| format!("{}: {e}", tracking_file.path))?;
121 }
122
123 Ok(())
124}
125
126pub fn remove_repos(mut repos: Vec<String>, tracking_file: &TrackingFile) -> Result<(), String> {
128 if tracking_file.contents.is_empty() {
129 return Err(String::from("No repository is being tracked"));
130 }
131
132 repos.sort_unstable();
134 repos.dedup();
135
136 let mut repos_ok = true;
137
138 for repo in &repos {
140 if !repo_is_tracked(repo.as_str(), tracking_file.contents.as_str()) {
142 eprintln!("{APP_NAME}: '{repo}' is not being tracked");
143 repos_ok = false;
144 }
145 }
146
147 if !repos_ok {
148 return Err(String::from("Repositories validation failed"));
149 }
150
151 let mut track_file_lines: Vec<&str> = tracking_file.contents.lines().collect();
152
153 let mut track_file = OpenOptions::new()
155 .write(true)
156 .truncate(true)
157 .open(tracking_file.path.clone())
158 .map_err(|e| format!("{}: {e}", tracking_file.path))?;
159
160 for repo in repos {
161 if let Some(last) = track_file_lines.last() {
163 if repo.trim() == last.trim() {
164 track_file_lines.pop();
165 }
166 else {
167 track_file_lines.retain(|&x| x.trim() != repo.trim());
168 }
169 }
170 }
171
172 track_file.write_all(track_file_lines.join("\n").as_bytes())
174 .map_err(|e| format!("{}: {e}", tracking_file.path))?;
175
176 Ok(())
177}
178
179pub fn remove_all(tracking_file: &TrackingFile) -> Result<(), String> {
181 if tracking_file.contents.is_empty() {
182 return Err(String::from("No repository is being tracked"));
183 }
184
185 fs::remove_file(tracking_file.path.clone()).map_err(|e| format!("{}: {e}", tracking_file.path))?;
186
187 Ok(())
188}
189
190pub async fn check_repos(mut repos: Vec<String>, flags: &[bool]) -> Result<(), String> {
193 repos.sort_unstable();
195 repos.dedup();
196
197 repos = repos_valid(repos.as_slice())?;
198
199 exec_async_check(repos, flags.to_vec()).await?;
200
201 Ok(())
202}
203
204pub async fn check_all(tracking_file: &TrackingFile, flags: &[bool]) -> Result<(), String> {
207 if tracking_file.contents.is_empty() {
208 return Err(String::from("No repository is being tracked"));
209 }
210
211 let track_file_lines: Vec<String> = tracking_file.contents
214 .lines()
215 .map(String::from)
216 .collect();
217
218 exec_async_check(track_file_lines, flags.to_vec()).await?;
219
220 Ok(())
221}