use anyhow::{bail, Context, Error};
use semver::Version;
use sha2::Digest;
use std::{
env, fs, io,
path::{Path, PathBuf},
process::Command,
};
use walkdir::{DirEntry, WalkDir};
pub(crate) fn signature(repo: &git2::Repository) -> Result<git2::Signature, Error> {
Ok(repo
.signature()
.or_else(|e| {
let name = env::var("GIT_AUTHOR_NAME").or_else(|_| env::var("GIT_COMMITTER_NAME"));
let email = env::var("GIT_AUTHOR_EMAIL").or_else(|_| env::var("GIT_COMMITTER_EMAIL"));
if name.is_err() || email.is_err() {
return Err(e);
}
git2::Signature::now(&name.unwrap(), &email.unwrap())
})
.with_context(|| {
"Could not determine git username/email for signature. \
Be sure to set `user.name` and `user.email` in gitconfig."
})?)
}
pub(crate) fn cargo_package(
manifest_path: &Path,
target_dir: &Path,
pkg: &cargo_metadata::Package,
package_args: Option<&Vec<String>>,
) -> Result<PathBuf, Error> {
let mut cmd = Command::new("cargo");
cmd.arg("package")
.current_dir(manifest_path.parent().unwrap());
if let Some(args) = package_args {
cmd.args(args);
}
let status = cmd.status().with_context(|| {
format!(
"Could not run `cargo package` for manifest {:?}.",
manifest_path
)
})?;
if !status.success() {
bail!("`cargo package` failed to run.");
}
let crate_path = target_dir
.join("package")
.join(&format!("{}-{}.crate", pkg.name, pkg.version));
if !crate_path.exists() {
bail!(
"Could not find crate after `cargo package` at {:?}",
crate_path
);
}
Ok(crate_path)
}
pub(crate) fn cksum(path: &Path) -> Result<String, Error> {
let mut hasher = sha2::Sha256::default();
let mut file = fs::File::open(&path)
.with_context(|| format!("Could not open crate file `{}`.", path.display()))?;
io::copy(&mut file, &mut hasher).unwrap();
Ok(hex::encode(hasher.finalize()))
}
pub(crate) fn extract_crate(crate_path: &Path) -> Result<(tempfile::TempDir, PathBuf), Error> {
let crate_file = fs::File::open(crate_path)
.with_context(|| format!("Failed to open `{}`.", crate_path.display()))?;
let tmp_dir = tempfile::tempdir().unwrap();
let gz = flate2::read::GzDecoder::new(crate_file);
let mut tar = tar::Archive::new(gz);
let prefix = crate_path.file_stem().unwrap();
for entry in tar.entries()? {
let mut entry = entry.with_context(|| "Failed to iterate over archive.")?;
let entry_path = entry
.path()
.with_context(|| "Failed to read entry path.")?
.into_owned();
if !entry_path.starts_with(prefix) {
bail!(
"Expected .crate file to contain entries rooted in `{}` directory, found `{}`.",
prefix.to_str().unwrap(),
entry_path.display()
);
}
entry
.unpack_in(tmp_dir.path())
.with_context(|| format!("Failed to unpack entry at `{}`.", entry_path.display()))?;
}
let pkg_path = tmp_dir.path().join(prefix);
Ok((tmp_dir, pkg_path))
}
pub(crate) fn pkg_path(name: &str) -> PathBuf {
let name = name.to_lowercase();
match name.len() {
1 => Path::new("1").join(&name),
2 => Path::new("2").join(&name),
3 => Path::new("3").join(&name[..1]).join(&name),
_ => Path::new(&name[0..2]).join(&name[2..4]).join(&name),
}
}
pub(crate) fn vers_eq(v1: &Version, v2: &Version) -> bool {
v1 == v2 && v1.build == v2.build
}
pub(crate) fn crate_walker(index: &Path) -> impl Iterator<Item = walkdir::Result<DirEntry>> {
WalkDir::new(index)
.into_iter()
.filter_entry(|e| {
let name = e.file_name();
name != "config.json" && name != ".git" && name != ".cargo-index-lock"
})
.filter(|e| match e {
Ok(e) => e.file_type().is_file(),
_ => true,
})
}