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
21use colored::Colorize;
22
23pub fn scan_dirs(mut dirs: Vec<String>, tracking_file: &TrackingFile, scan_hidden: bool) -> Result<String, String> {
25 dirs.sort_unstable();
27 dirs.dedup();
28
29 let mut dirs_ok = true;
32
33 for dir in &mut dirs {
34 let path = Path::new(&dir);
35
36 if let Ok(p) = path.try_exists() {
38 if !p {
39 eprintln!("{APP_NAME}: Directory '{dir}' does not exist");
40 dirs_ok = false;
41 continue;
42 }
43 }
44 else {
45 eprintln!("{APP_NAME}: Cannot check the existance of directory '{dir}'");
46 dirs_ok = false;
47 continue;
48 }
49
50 if path.is_file() {
52 eprintln!("{APP_NAME}: '{dir}' is not a directory");
53 dirs_ok = false;
54 }
55
56 if let Some(s) = fs::canonicalize(&dir)
59 .map_err(|e| format!("{dir}: {e}"))?
60 .to_str() {
61 *dir = s.to_string();
62 }
63 else {
64 eprintln!("{APP_NAME}: {dir}: The path contains invalid UTF-8 characters");
65 dirs_ok = false;
66 }
67 }
68
69 if !dirs_ok {
70 return Err(String::from("Directories validation failed"));
71 }
72
73 Ok(search_for_repos(dirs.as_slice(), tracking_file, scan_hidden)?)
74}
75
76pub fn scan_all(home_dir: String, tracking_file: &TrackingFile, scan_hidden: bool) -> Result<String, String> {
78 Ok(search_for_repos(&[home_dir], tracking_file, scan_hidden)?)
79}
80
81pub fn list(track_file_contents: &str) -> Result<(), String> {
83 if track_file_contents.is_empty() {
84 return Err(String::from("No repository is being tracked"));
85 }
86
87 print!("{}", track_file_contents.bold());
88
89 Ok(())
90}
91
92pub fn add(mut repos: Vec<String>, tracking_file: &TrackingFile) -> Result<(), String> {
94 repos.sort_unstable();
96 repos.dedup();
97
98 repos = repos_valid(repos.as_slice())?;
99
100 let mut track_file = OpenOptions::new()
102 .create(true)
103 .append(true)
104 .open(tracking_file.path.clone())
105 .map_err(|e| format!("{}: {e}", tracking_file.path))?;
106
107 for repo in repos {
108 if repo_is_tracked(repo.as_str(), tracking_file.contents.as_str()) {
111 println!("{APP_NAME}: '{repo}' is already being tracked");
112 continue;
113 }
114
115 track_file.write_all(
117 format!("{repo}\n").as_bytes())
118 .map_err(|e| format!("{}: {e}", tracking_file.path))?;
119 }
120
121 Ok(())
122}
123
124pub fn remove_repos(mut repos: Vec<String>, tracking_file: &TrackingFile) -> Result<(), String> {
126 if tracking_file.contents.is_empty() {
127 return Err(String::from("No repository is being tracked"));
128 }
129
130 repos.sort_unstable();
132 repos.dedup();
133
134 let mut repos_ok = true;
135
136 for repo in &repos {
138 if !repo_is_tracked(repo.as_str(), tracking_file.contents.as_str()) {
140 eprintln!("{APP_NAME}: '{repo}' is not being tracked");
141 repos_ok = false;
142 }
143 }
144
145 if !repos_ok {
146 return Err(String::from("Repositories validation failed"));
147 }
148
149 let mut track_file_lines: Vec<&str> = tracking_file.contents.lines().collect();
150
151 let mut track_file = OpenOptions::new()
153 .write(true)
154 .truncate(true)
155 .open(tracking_file.path.clone())
156 .map_err(|e| format!("{}: {e}", tracking_file.path))?;
157
158 for repo in repos {
159 if let Some(last) = track_file_lines.last() {
161 if repo.trim() == last.trim() {
162 track_file_lines.pop();
163 }
164 else {
165 track_file_lines.retain(|&x| x.trim() != repo.trim());
166 }
167 }
168 }
169
170 track_file.write_all(track_file_lines.join("\n").as_bytes())
172 .map_err(|e| format!("{}: {e}", tracking_file.path))?;
173
174 Ok(())
175}
176
177pub fn remove_all(tracking_file: &TrackingFile) -> Result<(), String> {
179 if tracking_file.contents.is_empty() {
180 return Err(String::from("No repository is being tracked"));
181 }
182
183 fs::remove_file(tracking_file.path.clone()).map_err(|e| format!("{}: {e}", tracking_file.path))?;
184
185 Ok(())
186}
187
188pub async fn check_repos(mut repos: Vec<String>, flags: &[bool]) -> Result<(), String> {
191 repos.sort_unstable();
193 repos.dedup();
194
195 repos = repos_valid(repos.as_slice())?;
196
197 exec_async_check(repos, flags.to_vec()).await?;
198
199 Ok(())
200}
201
202pub async fn check_all(tracking_file: &TrackingFile, flags: &[bool]) -> Result<(), String> {
205 if tracking_file.contents.is_empty() {
206 return Err(String::from("No repository is being tracked"));
207 }
208
209 let track_file_lines: Vec<String> = tracking_file.contents
212 .lines()
213 .map(String::from)
214 .collect();
215
216 exec_async_check(track_file_lines, flags.to_vec()).await?;
217
218 Ok(())
219}