From 4e17769868c116be8e0eaa2d82850cc4bf353172 Mon Sep 17 00:00:00 2001 From: Ubiratan Soares Date: Thu, 18 Apr 2024 10:25:07 +0200 Subject: [PATCH] Re-organises disk module (#25) --- src/disk.rs | 304 +---------------------- src/{filesystem.rs => disk/locations.rs} | 68 ++--- src/disk/operations.rs | 23 ++ src/disk/resources.rs | 300 ++++++++++++++++++++++ src/main.rs | 1 - src/wiper.rs | 18 +- 6 files changed, 368 insertions(+), 346 deletions(-) rename src/{filesystem.rs => disk/locations.rs} (87%) create mode 100644 src/disk/operations.rs create mode 100644 src/disk/resources.rs diff --git a/src/disk.rs b/src/disk.rs index a39867d..bf05f92 100644 --- a/src/disk.rs +++ b/src/disk.rs @@ -1,296 +1,14 @@ // Copyright 2024 Dotanuki Labs // SPDX-License-Identifier: MIT -use crate::models::{AllocatedResource, ProjectLevelDiskCache, UseCase, UserLevelDiskCache}; -use itertools::Itertools; -use std::path::{Path, PathBuf}; -use ubyte::ByteUnit; -use walkdir::{DirEntry, WalkDir}; - -pub fn usage_for_gradle_projects(projects: &[PathBuf]) -> anyhow::Result { - let use_case = UseCase::from(ProjectLevelDiskCache::BuildOutput); - let allocated = projects - .iter() - .map(|project| usage_for_gradle_project(project.as_path())) - .collect::>>()?; - - let total_amount = allocated - .iter() - .fold(ByteUnit::from(0), |total, allocation| total + allocation.amount); - - Ok(AllocatedResource::new(use_case, total_amount)) -} - -fn usage_for_gradle_project(gradle_project: &Path) -> anyhow::Result { - let use_case = UseCase::from(ProjectLevelDiskCache::BuildOutput); - - let Ok(true) = gradle_project.try_exists() else { - return Ok(AllocatedResource::new(use_case, ByteUnit::from(0))); - }; - - let total = WalkDir::new(gradle_project) - .into_iter() - .filter_map(|entry| entry.ok()) - .filter(ensure_build_output_file) - .map(|entry| size_for_entry(&entry)) - .sum::(); - - Ok(AllocatedResource::new(use_case, ByteUnit::from(total))) -} - -pub fn usage_for_maven_local(maven_local: &Path) -> anyhow::Result { - // We trust that M2 local repository lives under $HOME/.m2 - let use_case = UseCase::from(UserLevelDiskCache::MavenLocalRepository); - - let Ok(true) = maven_local.try_exists() else { - return Ok(AllocatedResource::new(use_case, ByteUnit::from(0))); - }; - - let total_amount = WalkDir::new(maven_local) - .into_iter() - .filter_map(|entry| entry.ok()) - .filter(ensure_file) - .map(|entry| size_for_entry(&entry)) - .sum::(); - - Ok(AllocatedResource::new(use_case, ByteUnit::from(total_amount))) -} - -pub fn usage_for_gradle_home(gradle_home: &Path) -> anyhow::Result> { - // We trust that gradle_home == $HOME/.gradle - let Ok(true) = gradle_home.try_exists() else { - return Ok(vec![]); - }; - - let total_per_use_case = WalkDir::new(gradle_home) - .into_iter() - .filter_map(|entry| entry.ok()) - .filter(ensure_file) - .map(|entry| (size_for_entry(&entry), evaluate_use_case_from_gradle_home(&entry))) - .filter(|item| item.1 != UseCase::from(UserLevelDiskCache::GradleOtherCaches)) - .group_by(|item| item.1) - .into_iter() - .map(|(use_case, group)| (use_case, group.fold(0, |total, (entry_size, _)| total + entry_size))) - .map(|(use_case, total)| AllocatedResource::new(use_case, ByteUnit::from(total))) - .sorted_by_key(|item| item.use_case) - .collect::>(); - - Ok(total_per_use_case) -} - -fn size_for_entry(entry: &DirEntry) -> u64 { - entry.metadata().expect("Expecting a valid metadata for entry").len() -} - -fn ensure_file(entry: &DirEntry) -> bool { - entry - .metadata() - .expect("Expecting a valid metadata for entry") - .is_file() -} - -fn ensure_build_output_file(entry: &DirEntry) -> bool { - let path = entry.path().to_str().expect("Expecting a valid path"); - let build_output = path.contains("build/"); - - let build_output_file = entry - .metadata() - .expect("Expecting a valid metadata for entry") - .is_file(); - - build_output && build_output_file -} - -fn evaluate_use_case_from_gradle_home(entry: &DirEntry) -> UseCase { - let raw_path = entry - .path() - .to_str() - .expect("Should be able to evaluate path as string"); - - // https://docs.gradle.org/current/userguide/configuration_cache.html - // https://docs.gradle.org/current/userguide/directory_layout.html - let cache_type = match raw_path { - _ if raw_path.contains(".gradle/caches") => UserLevelDiskCache::GradleBuildCaching, - _ if raw_path.contains(".gradle/configuration-cache") => UserLevelDiskCache::GradleConfigurationCaching, - _ if raw_path.contains(".gradle/daemon") => UserLevelDiskCache::GradleDaemonLogs, - _ if raw_path.contains(".gradle/jdks") => UserLevelDiskCache::GradleJDKToolchains, - _ if raw_path.contains(".gradle/wrapper") => UserLevelDiskCache::GradleDistributions, - _ if raw_path.contains(".gradle/.tmp") => UserLevelDiskCache::GradleTemporaryFiles, - _ if raw_path.contains(".gradle/native") => UserLevelDiskCache::GradleNativeFiles, - _ if raw_path.contains(".gradle/build-scan-data") => UserLevelDiskCache::GradleBuildScans, - _ => UserLevelDiskCache::GradleOtherCaches, - }; - - UseCase::from(cache_type) -} - -#[cfg(test)] -mod tests { - use crate::disk::{usage_for_gradle_home, usage_for_gradle_projects, usage_for_maven_local}; - use crate::models::{AllocatedResource, ProjectLevelDiskCache, UseCase, UserLevelDiskCache}; - use fake::{Fake, StringFaker}; - use std::fs; - use std::fs::File; - use std::io::Write; - use temp_dir::TempDir; - use ubyte::{ByteUnit, ToByteUnit}; - use uuid::Uuid; - - const CHARS: &str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - fn prepare_fake_gradle_home(dir: &TempDir) { - let folders = vec![ - ".gradle", - ".gradle/caches", - ".gradle/caches/build-cache-1", - ".gradle/configuration-cache", - ".gradle/daemon", - ".gradle/daemon/8.7", - ]; - - for folder in folders { - fs::create_dir(dir.path().join(folder)).expect("Cant create temporary fixture folder"); - } - } - - fn prepare_fake_gradle_projects(dir: &TempDir) { - let folders = vec![ - "AndroidStudioProjects", - "AndroidStudioProjects/my-project", - "AndroidStudioProjects/my-project/build", - "AndroidStudioProjects/my-project/.idea", - "AndroidStudioProjects/my-project/.gradle", - ]; - - for folder in folders { - fs::create_dir(dir.path().join(folder)).expect("Cant create temporary fixture folder"); - } - - let files = vec![ - "AndroidStudioProjects/my-project/settings.gradle", - "AndroidStudioProjects/my-project/build.gradle", - "AndroidStudioProjects/my-project/gradlew", - "AndroidStudioProjects/my-project/gradle.properties", - ]; - - for file in files { - fs::write(dir.path().join(file), "foo").expect("Cant create fixture file"); - } - } - - fn create_fake_1kb_file(gradle_home: &TempDir, folder: &str) { - let file_name = Uuid::new_v4(); - let relative_path = format!("{}/{}", folder, file_name); - let complete_path = gradle_home.path().join(relative_path); - - let faker = StringFaker::with(Vec::from(CHARS), 1000); - let fake: String = faker.fake(); - let mut fake_file = File::create(complete_path).expect("Cannot create temp file"); - fake_file - .write_all(fake.as_bytes()) - .expect("Cannot write into temp file"); - fake_file.sync_all().expect("Cannot sync temp file with FileSystem"); - } - - #[test] - fn should_compute_no_shared_caches_when_missing_gradle_home() { - let temp_dir = TempDir::new().expect("Cant create temp dir"); - let fake_gradle_home_path = temp_dir.path(); - - let usages = usage_for_gradle_home(fake_gradle_home_path).expect("Cannot compute use cases"); - - assert!(usages.is_empty()) - } - - #[test] - fn should_compute_shared_caches_when_gradle_home_has_content() { - let temp_dir = TempDir::new().expect("Cant create temp dir"); - - prepare_fake_gradle_home(&temp_dir); - - for _ in 0..2 { - create_fake_1kb_file(&temp_dir, ".gradle/daemon/8.7"); - } - - for _ in 0..3 { - create_fake_1kb_file(&temp_dir, ".gradle/caches/build-cache-1"); - } - - create_fake_1kb_file(&temp_dir, ".gradle/configuration-cache"); - - let fake_gradle_home_path = temp_dir.path(); - let usages = usage_for_gradle_home(fake_gradle_home_path).expect("Cannot compute use cases"); - - let expected = vec![ - AllocatedResource::new( - UseCase::from(UserLevelDiskCache::GradleConfigurationCaching), - 1.kilobytes(), - ), - AllocatedResource::new(UseCase::from(UserLevelDiskCache::GradleBuildCaching), 3.kilobytes()), - AllocatedResource::new(UseCase::from(UserLevelDiskCache::GradleDaemonLogs), 2.kilobytes()), - ]; - - assert_eq!(usages, expected); - } - - #[test] - fn should_compute_shared_caches_when_missing_maven_local() { - let temp_dir = TempDir::new().expect("Cant create temp dir"); - let fake_maven_local_path = temp_dir.path(); - - let usage = usage_for_maven_local(fake_maven_local_path).expect("Cannot compute use cases"); - - let use_case = UseCase::from(UserLevelDiskCache::MavenLocalRepository); - let expected = AllocatedResource::new(use_case, ByteUnit::from(0)); - assert_eq!(usage, expected) - } - - #[test] - fn should_compute_shared_caches_when_maven_local_present() { - let temp_dir = TempDir::new().expect("Cant create temp dir"); - let fake_maven_local_path = temp_dir.path(); - - fs::create_dir(temp_dir.path().join(".m2")).expect("Cant create temporary fixture folder"); - - for _ in 0..2 { - create_fake_1kb_file(&temp_dir, ".m2"); - } - - let usage = usage_for_maven_local(fake_maven_local_path).expect("Cannot compute use cases"); - - let use_case = UseCase::from(UserLevelDiskCache::MavenLocalRepository); - let expected = AllocatedResource::new(use_case, 2.kilobytes()); - assert_eq!(usage, expected) - } - - #[test] - fn should_not_compute_build_output_files_when_missing_gradle_projects() { - let temp_dir = TempDir::new().expect("Cant create temp dir"); - let fake_android_studio_projects_path = vec![temp_dir.path().to_path_buf()]; - - let usage = usage_for_gradle_projects(&fake_android_studio_projects_path).expect("Cannot compute use cases"); - - let use_case = UseCase::from(ProjectLevelDiskCache::BuildOutput); - let expected = AllocatedResource::new(use_case, ByteUnit::from(0)); - assert_eq!(usage, expected) - } - - #[test] - fn should_compute_build_output_files_when_gradle_projects_present() { - let temp_dir = TempDir::new().expect("Cant create temp dir"); - - prepare_fake_gradle_projects(&temp_dir); - - for _ in 0..5 { - create_fake_1kb_file(&temp_dir, "AndroidStudioProjects/my-project/build"); - } - - let fake_android_studio_projects_path = vec![temp_dir.path().to_path_buf()]; - - let usage = usage_for_gradle_projects(&fake_android_studio_projects_path).expect("Cannot compute use cases"); - - let use_case = UseCase::from(ProjectLevelDiskCache::BuildOutput); - let expected = AllocatedResource::new(use_case, 5.kilobytes()); - assert_eq!(usage, expected) - } -} +mod locations; +mod operations; +mod resources; + +pub use locations::find_all_gradle_projects; +pub use locations::find_gradle_home; +pub use locations::find_maven_local_repository; +pub use operations::cleanup; +pub use resources::resources_used_by_gradle_home; +pub use resources::resources_used_by_gradle_projects; +pub use resources::resources_used_by_maven_local_repository; diff --git a/src/filesystem.rs b/src/disk/locations.rs similarity index 87% rename from src/filesystem.rs rename to src/disk/locations.rs index 01ea62d..11ae842 100644 --- a/src/filesystem.rs +++ b/src/disk/locations.rs @@ -4,26 +4,39 @@ use crate::models::DiskCached; use anyhow::anyhow; use directories::BaseDirs; -use std::fs; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; -pub fn perform_cleanup(resources: &[DiskCached]) { - let paths_to_remove = resources.iter().flat_map(find_paths).collect::>(); +pub fn find_gradle_home() -> anyhow::Result { + let base_dir = BaseDirs::new().ok_or(anyhow!("Cannot access base directories"))?; + let home_dir = base_dir.home_dir(); + Ok(home_dir.join(".gradle")) +} - let errors = paths_to_remove - .iter() - .map(fs::remove_dir_all) - .filter(|deletion| deletion.is_err()) - .map(|deletion| deletion.expect_err("Expecting an error").to_string()) - .collect::>(); +pub fn find_maven_local_repository() -> anyhow::Result { + let base_dir = BaseDirs::new().ok_or(anyhow!("Cannot access base directories"))?; + let home_dir = base_dir.home_dir(); + Ok(home_dir.join(".m2")) +} - for error in errors { - eprintln!("{}", error); - } +pub fn find_all_gradle_projects() -> anyhow::Result> { + let base_dir = BaseDirs::new().ok_or(anyhow!("Cannot access base directories"))?; + let home_dir = base_dir.home_dir(); + + let android_studio_projects_folder = home_dir.join("AndroidStudioProjects"); + let android_projects = find_gradle_projects(android_studio_projects_folder.as_path())?; + + let intellij_projects_folder = home_dir.join("IdeaProjects"); + let jvm_projects = find_gradle_projects(intellij_projects_folder.as_path())?; + + let mut all_projects: Vec = Vec::new(); + all_projects.extend(android_projects); + all_projects.extend(jvm_projects); + + Ok(all_projects) } -fn find_paths(cached: &DiskCached) -> Vec { +pub(crate) fn find_paths(cached: &DiskCached) -> Vec { let base_dir = BaseDirs::new().expect("Cannot access base directories"); let home_dir = base_dir.home_dir(); @@ -57,35 +70,6 @@ fn find_paths(cached: &DiskCached) -> Vec { } } -pub fn find_gradle_home() -> anyhow::Result { - let base_dir = BaseDirs::new().ok_or(anyhow!("Cannot access base directories"))?; - let home_dir = base_dir.home_dir(); - Ok(home_dir.join(".gradle")) -} - -pub fn find_maven_local_repository() -> anyhow::Result { - let base_dir = BaseDirs::new().ok_or(anyhow!("Cannot access base directories"))?; - let home_dir = base_dir.home_dir(); - Ok(home_dir.join(".m2")) -} - -pub fn find_all_gradle_projects() -> anyhow::Result> { - let base_dir = BaseDirs::new().ok_or(anyhow!("Cannot access base directories"))?; - let home_dir = base_dir.home_dir(); - - let android_studio_projects_folder = home_dir.join("AndroidStudioProjects"); - let android_projects = find_gradle_projects(android_studio_projects_folder.as_path())?; - - let intellij_projects_folder = home_dir.join("IdeaProjects"); - let jvm_projects = find_gradle_projects(intellij_projects_folder.as_path())?; - - let mut all_projects: Vec = Vec::new(); - all_projects.extend(android_projects); - all_projects.extend(jvm_projects); - - Ok(all_projects) -} - fn find_gradle_projects(folder: &Path) -> anyhow::Result> { let projects = WalkDir::new(folder) .into_iter() diff --git a/src/disk/operations.rs b/src/disk/operations.rs new file mode 100644 index 0000000..10d0ca8 --- /dev/null +++ b/src/disk/operations.rs @@ -0,0 +1,23 @@ +// Copyright 2024 Dotanuki Labs +// SPDX-License-Identifier: MIT + +use crate::models::DiskCached; +use std::fs; + +pub fn cleanup(resources: &[DiskCached]) { + let paths_to_remove = resources + .iter() + .flat_map(crate::disk::locations::find_paths) + .collect::>(); + + let errors = paths_to_remove + .iter() + .map(fs::remove_dir_all) + .filter(|deletion| deletion.is_err()) + .map(|deletion| deletion.expect_err("Expecting an error").to_string()) + .collect::>(); + + for error in errors { + eprintln!("{}", error); + } +} diff --git a/src/disk/resources.rs b/src/disk/resources.rs new file mode 100644 index 0000000..c8841e2 --- /dev/null +++ b/src/disk/resources.rs @@ -0,0 +1,300 @@ +// Copyright 2024 Dotanuki Labs +// SPDX-License-Identifier: MIT + +use crate::models::{AllocatedResource, ProjectLevelDiskCache, UseCase, UserLevelDiskCache}; +use itertools::Itertools; +use std::path::{Path, PathBuf}; +use ubyte::ByteUnit; +use walkdir::{DirEntry, WalkDir}; + +pub fn resources_used_by_gradle_projects(projects: &[PathBuf]) -> anyhow::Result { + let use_case = UseCase::from(ProjectLevelDiskCache::BuildOutput); + let allocated = projects + .iter() + .map(|project| resources_used_per_gradle_project(project.as_path())) + .collect::>>()?; + + let total_amount = allocated + .iter() + .fold(ByteUnit::from(0), |total, allocation| total + allocation.amount); + + Ok(AllocatedResource::new(use_case, total_amount)) +} + +fn resources_used_per_gradle_project(gradle_project: &Path) -> anyhow::Result { + let use_case = UseCase::from(ProjectLevelDiskCache::BuildOutput); + + let Ok(true) = gradle_project.try_exists() else { + return Ok(AllocatedResource::new(use_case, ByteUnit::from(0))); + }; + + let total = WalkDir::new(gradle_project) + .into_iter() + .filter_map(|entry| entry.ok()) + .filter(ensure_build_output_file) + .map(|entry| size_for_entry(&entry)) + .sum::(); + + Ok(AllocatedResource::new(use_case, ByteUnit::from(total))) +} + +pub fn resources_used_by_maven_local_repository(maven_local: &Path) -> anyhow::Result { + // We trust that M2 local repository lives under $HOME/.m2 + let use_case = UseCase::from(UserLevelDiskCache::MavenLocalRepository); + + let Ok(true) = maven_local.try_exists() else { + return Ok(AllocatedResource::new(use_case, ByteUnit::from(0))); + }; + + let total_amount = WalkDir::new(maven_local) + .into_iter() + .filter_map(|entry| entry.ok()) + .filter(ensure_file) + .map(|entry| size_for_entry(&entry)) + .sum::(); + + Ok(AllocatedResource::new(use_case, ByteUnit::from(total_amount))) +} + +pub fn resources_used_by_gradle_home(gradle_home: &Path) -> anyhow::Result> { + // We trust that gradle_home == $HOME/.gradle + let Ok(true) = gradle_home.try_exists() else { + return Ok(vec![]); + }; + + let total_per_use_case = WalkDir::new(gradle_home) + .into_iter() + .filter_map(|entry| entry.ok()) + .filter(ensure_file) + .map(|entry| (size_for_entry(&entry), evaluate_use_case_from_gradle_home(&entry))) + .filter(|item| item.1 != UseCase::from(UserLevelDiskCache::GradleOtherCaches)) + .group_by(|item| item.1) + .into_iter() + .map(|(use_case, group)| (use_case, group.fold(0, |total, (entry_size, _)| total + entry_size))) + .map(|(use_case, total)| AllocatedResource::new(use_case, ByteUnit::from(total))) + .sorted_by_key(|item| item.use_case) + .collect::>(); + + Ok(total_per_use_case) +} + +fn size_for_entry(entry: &DirEntry) -> u64 { + entry.metadata().expect("Expecting a valid metadata for entry").len() +} + +fn ensure_file(entry: &DirEntry) -> bool { + entry + .metadata() + .expect("Expecting a valid metadata for entry") + .is_file() +} + +fn ensure_build_output_file(entry: &DirEntry) -> bool { + let path = entry.path().to_str().expect("Expecting a valid path"); + let build_output = path.contains("build/"); + + let build_output_file = entry + .metadata() + .expect("Expecting a valid metadata for entry") + .is_file(); + + build_output && build_output_file +} + +fn evaluate_use_case_from_gradle_home(entry: &DirEntry) -> UseCase { + let raw_path = entry + .path() + .to_str() + .expect("Should be able to evaluate path as string"); + + // https://docs.gradle.org/current/userguide/configuration_cache.html + // https://docs.gradle.org/current/userguide/directory_layout.html + let cache_type = match raw_path { + _ if raw_path.contains(".gradle/caches") => UserLevelDiskCache::GradleBuildCaching, + _ if raw_path.contains(".gradle/configuration-cache") => UserLevelDiskCache::GradleConfigurationCaching, + _ if raw_path.contains(".gradle/daemon") => UserLevelDiskCache::GradleDaemonLogs, + _ if raw_path.contains(".gradle/jdks") => UserLevelDiskCache::GradleJDKToolchains, + _ if raw_path.contains(".gradle/wrapper") => UserLevelDiskCache::GradleDistributions, + _ if raw_path.contains(".gradle/.tmp") => UserLevelDiskCache::GradleTemporaryFiles, + _ if raw_path.contains(".gradle/native") => UserLevelDiskCache::GradleNativeFiles, + _ if raw_path.contains(".gradle/build-scan-data") => UserLevelDiskCache::GradleBuildScans, + _ => UserLevelDiskCache::GradleOtherCaches, + }; + + UseCase::from(cache_type) +} + +#[cfg(test)] +mod tests { + use crate::disk::resources::{ + resources_used_by_gradle_home, resources_used_by_gradle_projects, resources_used_by_maven_local_repository, + }; + use crate::models::{AllocatedResource, ProjectLevelDiskCache, UseCase, UserLevelDiskCache}; + use fake::{Fake, StringFaker}; + use std::fs; + use std::fs::File; + use std::io::Write; + use temp_dir::TempDir; + use ubyte::{ByteUnit, ToByteUnit}; + use uuid::Uuid; + + const CHARS: &str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + fn prepare_fake_gradle_home(dir: &TempDir) { + let folders = vec![ + ".gradle", + ".gradle/caches", + ".gradle/caches/build-cache-1", + ".gradle/configuration-cache", + ".gradle/daemon", + ".gradle/daemon/8.7", + ]; + + for folder in folders { + fs::create_dir(dir.path().join(folder)).expect("Cant create temporary fixture folder"); + } + } + + fn prepare_fake_gradle_projects(dir: &TempDir) { + let folders = vec![ + "AndroidStudioProjects", + "AndroidStudioProjects/my-project", + "AndroidStudioProjects/my-project/build", + "AndroidStudioProjects/my-project/.idea", + "AndroidStudioProjects/my-project/.gradle", + ]; + + for folder in folders { + fs::create_dir(dir.path().join(folder)).expect("Cant create temporary fixture folder"); + } + + let files = vec![ + "AndroidStudioProjects/my-project/settings.gradle", + "AndroidStudioProjects/my-project/build.gradle", + "AndroidStudioProjects/my-project/gradlew", + "AndroidStudioProjects/my-project/gradle.properties", + ]; + + for file in files { + fs::write(dir.path().join(file), "foo").expect("Cant create fixture file"); + } + } + + fn create_fake_1kb_file(gradle_home: &TempDir, folder: &str) { + let file_name = Uuid::new_v4(); + let relative_path = format!("{}/{}", folder, file_name); + let complete_path = gradle_home.path().join(relative_path); + + let faker = StringFaker::with(Vec::from(CHARS), 1000); + let fake: String = faker.fake(); + let mut fake_file = File::create(complete_path).expect("Cannot create temp file"); + fake_file + .write_all(fake.as_bytes()) + .expect("Cannot write into temp file"); + fake_file.sync_all().expect("Cannot sync temp file with FileSystem"); + } + + #[test] + fn should_compute_no_shared_caches_when_missing_gradle_home() { + let temp_dir = TempDir::new().expect("Cant create temp dir"); + let fake_gradle_home_path = temp_dir.path(); + + let usages = resources_used_by_gradle_home(fake_gradle_home_path).expect("Cannot compute use cases"); + + assert!(usages.is_empty()) + } + + #[test] + fn should_compute_shared_caches_when_gradle_home_has_content() { + let temp_dir = TempDir::new().expect("Cant create temp dir"); + + prepare_fake_gradle_home(&temp_dir); + + for _ in 0..2 { + create_fake_1kb_file(&temp_dir, ".gradle/daemon/8.7"); + } + + for _ in 0..3 { + create_fake_1kb_file(&temp_dir, ".gradle/caches/build-cache-1"); + } + + create_fake_1kb_file(&temp_dir, ".gradle/configuration-cache"); + + let fake_gradle_home_path = temp_dir.path(); + let usages = resources_used_by_gradle_home(fake_gradle_home_path).expect("Cannot compute use cases"); + + let expected = vec![ + AllocatedResource::new( + UseCase::from(UserLevelDiskCache::GradleConfigurationCaching), + 1.kilobytes(), + ), + AllocatedResource::new(UseCase::from(UserLevelDiskCache::GradleBuildCaching), 3.kilobytes()), + AllocatedResource::new(UseCase::from(UserLevelDiskCache::GradleDaemonLogs), 2.kilobytes()), + ]; + + assert_eq!(usages, expected); + } + + #[test] + fn should_compute_shared_caches_when_missing_maven_local() { + let temp_dir = TempDir::new().expect("Cant create temp dir"); + let fake_maven_local_path = temp_dir.path(); + + let usage = resources_used_by_maven_local_repository(fake_maven_local_path).expect("Cannot compute use cases"); + + let use_case = UseCase::from(UserLevelDiskCache::MavenLocalRepository); + let expected = AllocatedResource::new(use_case, ByteUnit::from(0)); + assert_eq!(usage, expected) + } + + #[test] + fn should_compute_shared_caches_when_maven_local_present() { + let temp_dir = TempDir::new().expect("Cant create temp dir"); + let fake_maven_local_path = temp_dir.path(); + + fs::create_dir(temp_dir.path().join(".m2")).expect("Cant create temporary fixture folder"); + + for _ in 0..2 { + create_fake_1kb_file(&temp_dir, ".m2"); + } + + let usage = resources_used_by_maven_local_repository(fake_maven_local_path).expect("Cannot compute use cases"); + + let use_case = UseCase::from(UserLevelDiskCache::MavenLocalRepository); + let expected = AllocatedResource::new(use_case, 2.kilobytes()); + assert_eq!(usage, expected) + } + + #[test] + fn should_not_compute_build_output_files_when_missing_gradle_projects() { + let temp_dir = TempDir::new().expect("Cant create temp dir"); + let fake_android_studio_projects_path = vec![temp_dir.path().to_path_buf()]; + + let usage = + resources_used_by_gradle_projects(&fake_android_studio_projects_path).expect("Cannot compute use cases"); + + let use_case = UseCase::from(ProjectLevelDiskCache::BuildOutput); + let expected = AllocatedResource::new(use_case, ByteUnit::from(0)); + assert_eq!(usage, expected) + } + + #[test] + fn should_compute_build_output_files_when_gradle_projects_present() { + let temp_dir = TempDir::new().expect("Cant create temp dir"); + + prepare_fake_gradle_projects(&temp_dir); + + for _ in 0..5 { + create_fake_1kb_file(&temp_dir, "AndroidStudioProjects/my-project/build"); + } + + let fake_android_studio_projects_path = vec![temp_dir.path().to_path_buf()]; + + let usage = + resources_used_by_gradle_projects(&fake_android_studio_projects_path).expect("Cannot compute use cases"); + + let use_case = UseCase::from(ProjectLevelDiskCache::BuildOutput); + let expected = AllocatedResource::new(use_case, 5.kilobytes()); + assert_eq!(usage, expected) + } +} diff --git a/src/main.rs b/src/main.rs index 4e4c63b..e6a5f57 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,6 @@ mod cli; mod disk; -mod filesystem; mod models; mod wiper; diff --git a/src/wiper.rs b/src/wiper.rs index 5ec7533..017d239 100644 --- a/src/wiper.rs +++ b/src/wiper.rs @@ -1,9 +1,7 @@ // Copyright 2024 Dotanuki Labs // SPDX-License-Identifier: MIT -use crate::disk::{usage_for_gradle_home, usage_for_gradle_projects, usage_for_maven_local}; -use crate::filesystem; -use crate::filesystem::{find_all_gradle_projects, find_gradle_home, find_maven_local_repository}; +use crate::disk; use crate::models::{ AllocatedResource, DiskCached, EvaluationOutcome, ExecutionOutcome, MachineResource, UserLevelDiskCache, WipeAction, WipingOutcome, @@ -39,16 +37,16 @@ fn deep_wipe_ram() -> anyhow::Result { } fn evaluate_disk_space() -> anyhow::Result { - let gradle_home = find_gradle_home()?; - let gradle_home_resources = usage_for_gradle_home(gradle_home.as_path())?; + let gradle_home = disk::find_gradle_home()?; + let gradle_home_resources = disk::resources_used_by_gradle_home(gradle_home.as_path())?; let total_size_for_gradle_home = calculate_total_allocated(&gradle_home_resources); - let maven_local_repository = find_maven_local_repository()?; - let maven_local_resources = usage_for_maven_local(maven_local_repository.as_path())?; + let maven_local_repository = disk::find_maven_local_repository()?; + let maven_local_resources = disk::resources_used_by_maven_local_repository(maven_local_repository.as_path())?; let total_size_for_maven_local = maven_local_resources.amount; - let gradle_projects = find_all_gradle_projects()?; - let gradle_projects_resources = usage_for_gradle_projects(&gradle_projects)?; + let gradle_projects = disk::find_all_gradle_projects()?; + let gradle_projects_resources = disk::resources_used_by_gradle_projects(&gradle_projects)?; let total_size_for_gradle_projects = gradle_projects_resources.amount; let mut disk_resources: Vec = Vec::new(); @@ -75,7 +73,7 @@ fn shallow_wipe_disk() -> anyhow::Result { DiskCached::Shared(UserLevelDiskCache::MavenLocalRepository), ]; - filesystem::perform_cleanup(&caches_to_remove); + disk::cleanup(&caches_to_remove); let after_cleaning = match evaluate_disk_space()? { ExecutionOutcome::Evaluation(outcome) => outcome.total_size,