#![allow(clippy::needless_collect)]
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use crate::cache::caches::*;
use crate::cache::*;
use crate::library::*;
use crate::library::{CargoCachePaths, Error};
use crate::remove::*;
use cargo_metadata::{CargoOpt, MetadataCommand};
#[derive(Debug, Clone, PartialEq, Eq)]
enum SourceKind {
Crate(PathBuf),
Git(PathBuf),
}
impl SourceKind {
fn inner(self) -> PathBuf {
match self {
SourceKind::Crate(p) | SourceKind::Git(p) => p,
}
}
}
fn find_crate_name_git(toml_path: &Path, cargo_home: &Path) -> Option<SourceKind> {
let v: Vec<&OsStr> = toml_path.iter().collect();
let checkouts_pos = v.iter().position(|i| i == &"checkouts")?;
let path_segments = &v[(checkouts_pos - 1)..(checkouts_pos + 3)];
let mut path = cargo_home.to_path_buf();
path_segments.iter().for_each(|p| path.push(p));
Some(SourceKind::Git(path))
}
fn find_crate_name_crate(toml_path: &Path, cargo_home: &Path) -> Option<SourceKind> {
let v: Vec<&OsStr> = toml_path.iter().collect();
let registry_pos = v.iter().position(|i| i == &"registry")?;
let path_segments = &v[(registry_pos)..(registry_pos + 4)];
let mut path = cargo_home.to_path_buf();
path_segments.iter().for_each(|p| path.push(p));
Some(SourceKind::Crate(path))
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn clean_unref(
cargo_cache_paths: &CargoCachePaths,
manifest_path: Option<&str>,
bin_cache: &mut bin::BinaryCache,
checkouts_cache: &mut git_checkouts::GitCheckoutCache,
bare_repos_cache: &mut git_bare_repos::GitRepoCache,
registry_pkg_caches: &mut registry_pkg_cache::RegistryPkgCaches,
registry_index_caches: &mut registry_index::RegistryIndicesCache,
registry_sources_caches: &mut registry_sources::RegistrySourceCaches,
dry_run: bool,
size_changed: &mut bool,
) -> Result<(), Error> {
let original_total_cache_size = bin_cache.total_size()
+ checkouts_cache.total_size()
+ bare_repos_cache.total_size()
+ registry_pkg_caches.total_size()
+ registry_index_caches.total_size()
+ registry_sources_caches.total_size();
let cargo_home = &cargo_cache_paths.cargo_home;
let manifest = match manifest_path {
Some(path_str) => PathBuf::from(path_str),
None => crate::local::get_manifest()?,
};
let metadata = MetadataCommand::new()
.manifest_path(&manifest)
.features(CargoOpt::AllFeatures)
.exec()
.map_err(|e| Error::UnparsableManifest(manifest, e))?;
let dependencies = metadata.packages;
#[allow(clippy::manual_filter_map)]
let required_packages = dependencies
.iter()
.map(|pkg| PathBuf::from(&pkg.manifest_path))
.filter(|toml_path| toml_path.starts_with(cargo_home))
.map(|toml_path| {
if toml_path.starts_with(&cargo_cache_paths.git_checkouts) {
find_crate_name_git(&toml_path, cargo_home).unwrap_or_else(|| {
panic!("Failed to find 'checkouts' in {} ", toml_path.display())
})
} else if toml_path.starts_with(&cargo_cache_paths.registry_sources) {
find_crate_name_crate(&toml_path, cargo_home).unwrap_or_else(|| {
panic!("Failed to find 'registry' in {} ", toml_path.display())
})
} else {
panic!("Failed to parse toml path: '{}'", toml_path.display());
}
})
.map(|sourcekind| match sourcekind {
SourceKind::Crate(registry_src_path) => {
let path = registry_src_path.iter().collect::<Vec<&OsStr>>();
let package_name = path[path.len() - 1];
let registry = path[path.len() - 2];
let mut registry_cache_path = cargo_cache_paths.registry_pkg_cache.clone();
registry_cache_path.push(registry);
registry_cache_path.push(format!(
"{}{}",
package_name.to_os_string().into_string().unwrap(),
".crate"
));
SourceKind::Crate(registry_cache_path)
}
SourceKind::Git(gitpath) => {
let mut repo_name = gitpath;
let _ = repo_name.pop(); let repo_name = repo_name.iter().last().unwrap();
let mut db_name = cargo_cache_paths.git_repos_bare.clone();
db_name.push(repo_name);
SourceKind::Git(db_name)
}
});
remove_file(
&cargo_cache_paths.git_checkouts,
dry_run,
size_changed,
None,
&DryRunMessage::Default,
Some(checkouts_cache.total_size()),
);
checkouts_cache.invalidate();
remove_file(
&cargo_cache_paths.registry_sources,
dry_run,
size_changed,
None,
&DryRunMessage::Default,
Some(registry_sources_caches.total_size()),
);
registry_sources_caches.invalidate();
let (required_crates, required_git_repos): (Vec<SourceKind>, Vec<SourceKind>) =
required_packages.partition(|dep| match dep {
SourceKind::Crate(_) => true,
SourceKind::Git(_) => false,
});
let required_crates: Vec<_> = required_crates.into_iter().map(SourceKind::inner).collect();
let required_git_repos: Vec<_> = required_git_repos
.into_iter()
.map(SourceKind::inner)
.collect();
let bare_repos = bare_repos_cache.items();
let mut crates = Vec::new();
for cache in registry_pkg_caches.caches() {
crates.extend(cache.files());
}
bare_repos
.iter()
.filter(|repo_in_cache|
!required_git_repos.contains(repo_in_cache))
.for_each(|repo| {
remove_file(
repo,
dry_run,
size_changed,
None,
&DryRunMessage::Default,
Some(size_of_path(repo)),
);
});
crates
.iter()
.filter(|crate_in_cache|
!required_crates.contains(crate_in_cache))
.for_each(|krate| {
remove_file(
krate,
dry_run,
size_changed,
None,
&DryRunMessage::Default,
Some(size_of_path(krate)),
);
});
bare_repos_cache.invalidate();
registry_pkg_caches.invalidate();
print_size_changed_summary(
original_total_cache_size,
cargo_cache_paths,
bin_cache,
checkouts_cache,
bare_repos_cache,
registry_pkg_caches,
registry_index_caches,
registry_sources_caches,
);
Ok(())
}
#[cfg(test)]
mod clitests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn sourcekind_inner() {
let sk_crate = SourceKind::Crate(PathBuf::from("abc"));
assert_eq!(sk_crate.inner(), PathBuf::from("abc"));
let sk_git = SourceKind::Git(PathBuf::from("def"));
assert_eq!(sk_git.inner(), PathBuf::from("def"));
}
#[test]
fn crate_name_git_some() {
let toml_path =
PathBuf::from(".cargo/git/checkouts/home-fb9469891e5cfbe6/3a6eccd/Cargo.toml");
let cargo_home = PathBuf::from(".cargo/");
let name = find_crate_name_git(&toml_path, &cargo_home);
assert_eq!(
name,
Some(SourceKind::Git(PathBuf::from(
".cargo/git/checkouts/home-fb9469891e5cfbe6/3a6eccd/",
))),
);
}
#[test]
fn crate_name_git_none() {
let toml_path =
PathBuf::from(".cargo/git/failuretoparse/home-fb9469891e5cfbe6/3a6eccd/Cargo.toml");
let cargo_home = PathBuf::from(".cargo/");
let name = find_crate_name_git(&toml_path, &cargo_home);
assert_eq!(name, None);
}
#[test]
fn crate_name_crate_some() {
let toml_path = PathBuf::from(
".cargo/registry/src/github.com-1ecc6299db9ec823/winapi-0.3.8/Cargo.toml",
);
let cargo_home = PathBuf::from(".cargo/");
let name = find_crate_name_crate(&toml_path, &cargo_home);
assert_eq!(
name,
Some(SourceKind::Crate(PathBuf::from(
".cargo/registry/src/github.com-1ecc6299db9ec823/winapi-0.3.8/",
))),
);
}
#[test]
fn crate_name_crate_none() {
let toml_path = PathBuf::from(
".cargo/AAAAAAHH/src/github.com-1ecc6299db9ec823/winapi-0.3.8/Cargo.toml",
);
let cargo_home = PathBuf::from(".cargo/");
let name = find_crate_name_crate(&toml_path, &cargo_home);
assert_eq!(name, None,);
}
}