|
@@ -0,0 +1,115 @@
|
|
|
+use std::{fs, process, path::PathBuf, time::SystemTime};
|
|
|
+use anyhow::Result as anyResult;
|
|
|
+
|
|
|
+pub const SECS_IN_DAY: u64 = 86400;
|
|
|
+
|
|
|
+pub fn find_target_dir(path: PathBuf) -> anyResult<bool> {
|
|
|
+ let mut found_target_dir: bool = false;
|
|
|
+ let mut found_cargo_toml: bool = false;
|
|
|
+ for entry in fs::read_dir(path)? {
|
|
|
+ let entry = entry?;
|
|
|
+ let p = entry.path();
|
|
|
+ if p.is_dir() && p.ends_with("target") {
|
|
|
+ found_target_dir = true;
|
|
|
+ } else if p.is_file() && p.file_name().unwrap() == "Cargo.toml" {
|
|
|
+ found_cargo_toml = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Ok(found_target_dir && found_cargo_toml)
|
|
|
+}
|
|
|
+
|
|
|
+pub fn find_newest_file(path: PathBuf) -> anyResult<PathBuf> {
|
|
|
+ let mut pat: PathBuf = path.clone();
|
|
|
+ let mut pat_dif: SystemTime = SystemTime::now();
|
|
|
+ for entry in fs::read_dir(&path)? {
|
|
|
+ let entry = entry?;
|
|
|
+ let p = entry.path();
|
|
|
+ if !p.is_file() {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ let meta = entry.metadata()?;
|
|
|
+ let modif = meta.modified()?;
|
|
|
+ let access = meta.accessed()?;
|
|
|
+ if pat_dif.elapsed()?.as_secs() == 0 {
|
|
|
+ pat = path.join(p);
|
|
|
+ if modif.ge(&access) {
|
|
|
+ pat_dif = modif;
|
|
|
+ } else {
|
|
|
+ pat_dif = access;
|
|
|
+ }
|
|
|
+ } else if pat_dif.le(&modif) || pat_dif.le(&access) {
|
|
|
+ pat = path.join(p);
|
|
|
+ if modif.ge(&access) {
|
|
|
+ pat_dif = modif;
|
|
|
+ } else {
|
|
|
+ pat_dif = access;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Ok(pat)
|
|
|
+}
|
|
|
+
|
|
|
+pub fn execute_clean(path: PathBuf) -> anyResult<()> {
|
|
|
+ let _cmd = process::Command::new("cargo")
|
|
|
+ .current_dir(path)
|
|
|
+ .arg("clean")
|
|
|
+ .status()?;
|
|
|
+ Ok(())
|
|
|
+}
|
|
|
+
|
|
|
+pub fn clean_in_dir(path: PathBuf, forced: bool, days: u64) -> anyResult<u64> {
|
|
|
+ let mut cleaned: u64 = 0;
|
|
|
+ for entry in fs::read_dir(path)? {
|
|
|
+ let entry = entry?;
|
|
|
+ let p = entry.path();
|
|
|
+ if p.is_dir() {
|
|
|
+ let needed = find_target_dir(p.clone())?;
|
|
|
+ if needed {
|
|
|
+ if !forced {
|
|
|
+ // If forced no need to check
|
|
|
+ let newest = find_newest_file(p.clone())?;
|
|
|
+ let meta = fs::metadata(newest)?;
|
|
|
+ let modif = meta.modified()?;
|
|
|
+ let access = meta.accessed()?;
|
|
|
+ let el: u64;
|
|
|
+ if modif.ge(&access) {
|
|
|
+ el = modif.elapsed()?.as_secs();
|
|
|
+ } else {
|
|
|
+ el = access.elapsed()?.as_secs();
|
|
|
+ }
|
|
|
+ let d = el / SECS_IN_DAY;
|
|
|
+ if d < days {
|
|
|
+ println!(" => {} (dirty, {} days)", p.display(), d);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ println!(" => {} (dirty)", p.display());
|
|
|
+ execute_clean(p)?;
|
|
|
+ cleaned += 1;
|
|
|
+ } else {
|
|
|
+ // Maybe nested?
|
|
|
+ let r = clean_in_dir(p, forced, days)?;
|
|
|
+ cleaned += r;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Ok(cleaned)
|
|
|
+}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod tests {
|
|
|
+ use std::{fs, path::PathBuf};
|
|
|
+ use anyhow::Result as anyResult;
|
|
|
+ use crate::{find_target_dir, find_newest_file, execute_clean, clean_in_dir};
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn locate_target_dir() -> anyResult<()> {
|
|
|
+ fs::create_dir(PathBuf::from("test"))?;
|
|
|
+ fs::create_dir(PathBuf::from("test/target"))?;
|
|
|
+ fs::write(PathBuf::from("test/Cargo.toml"), "")?;
|
|
|
+ let found = find_target_dir(PathBuf::from("test"))?;
|
|
|
+ fs::remove_dir_all(PathBuf::from("test"))?;
|
|
|
+ assert!(found, "Should have found the target dir");
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+}
|