use crate::{
list::_list,
lock::Lock,
metadata::{metadata_reg, MetaInfo},
util::{extract_crate, pkg_path, signature},
IndexPackage,
};
use anyhow::{bail, Context, Error};
use semver::{Comparator, Op, VersionReq};
use std::fs::File;
use std::{fs, io::Write, path::Path};
pub fn add(
index_path: impl AsRef<Path>,
index_url: &str,
manifest_path: Option<&Path>,
upload: Option<&str>,
package_args: Option<&Vec<String>>,
) -> Result<IndexPackage, Error> {
add_reg(
index_path,
index_url,
manifest_path,
None,
upload,
package_args,
)
}
pub fn force_add(
index_path: impl AsRef<Path>,
index_url: &str,
manifest_path: Option<&Path>,
upload: Option<&str>,
package_args: Option<&Vec<String>>,
) -> Result<IndexPackage, Error> {
force_add_reg(
index_path,
index_url,
manifest_path,
None,
upload,
package_args,
)
}
pub(crate) fn add_reg(
index_path: impl AsRef<Path>,
index_url: &str,
manifest_path: Option<&Path>,
crate_path: Option<&Path>,
upload: Option<&str>,
package_args: Option<&Vec<String>>,
) -> Result<IndexPackage, Error> {
let meta_info = metadata_reg(index_url, manifest_path, crate_path, package_args)?;
let index_pkg = meta_info.index_pkg;
let index_path = index_path.as_ref();
let matching_pkgs = _list(
index_path,
&index_pkg.name,
Some(&VersionReq {
comparators: vec![Comparator {
op: Op::Exact,
major: index_pkg.vers.major,
minor: Some(index_pkg.vers.minor),
patch: Some(index_pkg.vers.patch),
pre: index_pkg.vers.pre.clone(),
}],
}),
)?;
if !matching_pkgs.is_empty() {
bail!(
"Package `{}` version `{}` is already in the index.",
index_pkg.name,
index_pkg.vers
);
}
update_crate_index(
index_path,
index_url,
manifest_path,
crate_path,
upload,
package_args,
)
}
pub(crate) fn force_add_reg(
index_path: impl AsRef<Path>,
index_url: &str,
manifest_path: Option<&Path>,
crate_path: Option<&Path>,
upload: Option<&str>,
package_args: Option<&Vec<String>>,
) -> Result<IndexPackage, Error> {
update_crate_index(
index_path,
index_url,
manifest_path,
crate_path,
upload,
package_args,
)
}
fn update_crate_index(
index_path: impl AsRef<Path>,
index_url: &str,
manifest_path: Option<&Path>,
crate_path: Option<&Path>,
upload: Option<&str>,
package_args: Option<&Vec<String>>,
) -> Result<IndexPackage, Error> {
let MetaInfo {
index_pkg,
crate_path,
} = metadata_reg(index_url, manifest_path, crate_path, package_args)?;
let index_path = index_path.as_ref();
let repo = git2::Repository::open(index_path)
.with_context(|| format!("Could not open index at `{}`.", index_path.display()))?;
let lock = Lock::new_exclusive(index_path)?;
let all_pkg_vers = _list(index_path, &index_pkg.name, None)?;
let pkg_vers_exists = all_pkg_vers
.iter()
.any(|pkg_vers| pkg_vers.vers == index_pkg.vers);
for dep in &index_pkg.deps {
if dep.registry.is_none() {
let dep_name = dep.package.as_ref().unwrap_or(&dep.name);
let matching_deps = _list(index_path, dep_name, Some(&dep.req))?;
if matching_deps.is_empty() {
bail!(
"Package `{}` dependency `{}:{}` not found in index.",
index_pkg.name,
dep_name,
dep.req
);
}
}
}
let repo_path = pkg_path(&index_pkg.name);
let path = index_path.join(&repo_path);
let dir_path = path.parent().unwrap();
fs::create_dir_all(&dir_path)
.with_context(|| format!("Failed to create directory `{}`.", dir_path.display()))?;
let mut f = fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(&path)
.with_context(|| format!("Failed to create or open `{}`.", path.display()))?;
for pkg_vers in all_pkg_vers {
if pkg_vers.vers == index_pkg.vers {
write_index_pkg(&mut f, &index_pkg)
} else {
write_index_pkg(&mut f, &pkg_vers)
}
.with_context(|| format!("Failed to write json entry at `{}`.", path.display()))?;
}
if !pkg_vers_exists {
write_index_pkg(&mut f, &index_pkg)
.with_context(|| format!("Failed to write json entry at `{}`.", path.display()))?;
}
let msg = format!("Updating crate `{}#{}`", index_pkg.name, index_pkg.vers);
if let Some(upload) = upload {
let replaced = upload
.replace("{crate}", &index_pkg.name)
.replace("{version}", &index_pkg.vers.to_string());
let upload = Path::new(&replaced);
fs::create_dir_all(upload)?;
fs::copy(&crate_path, upload.join(&crate_path.file_name().unwrap()))?;
}
git_add(&repo, &repo_path, &msg).with_context(|| "Failed to add to git repo.")?;
drop(lock);
Ok(index_pkg)
}
fn write_index_pkg(f: &mut File, index_pkg: &IndexPackage) -> std::io::Result<()> {
let mut meta_json = serde_json::to_string(&index_pkg)?;
meta_json.push('\n');
f.write_all(meta_json.as_bytes())
}
pub(crate) fn git_add(repo: &git2::Repository, path: &Path, msg: &str) -> Result<(), Error> {
let mut index = repo.index()?;
index.add_path(path)?;
index.write()?;
let id = index.write_tree()?;
let tree = repo.find_tree(id)?;
let head = repo.head()?;
let parent = repo.find_commit(head.target().unwrap())?;
let sig = signature(repo)?;
repo.commit(Some("HEAD"), &sig, &sig, msg, &tree, &[&parent])?;
Ok(())
}
pub fn add_from_crate(
index_path: impl AsRef<Path>,
index_url: &str,
crate_path: impl AsRef<Path>,
upload: Option<&str>,
) -> Result<IndexPackage, Error> {
let crate_path = crate_path.as_ref();
let (_tmp_dir, pkg_path) = extract_crate(crate_path)?;
let manifest_path = pkg_path.join("Cargo.toml");
add_reg(
index_path,
index_url,
Some(&manifest_path),
Some(crate_path),
upload,
None,
)
}