mod manifest;
mod metadata;
mod profile;
#[doc(inline)]
pub use self::{
manifest::{Manifest, ManifestPath},
profile::Profile,
};
use anyhow::Result;
use cargo_metadata::{Metadata as CargoMetadata, Package, PackageId};
use std::{
collections::HashMap,
path::{Path, PathBuf},
};
pub struct Workspace {
workspace_root: PathBuf,
root_package: PackageId,
members: HashMap<PackageId, (Package, Manifest)>,
}
impl Workspace {
pub fn new(metadata: &CargoMetadata, root_package: &PackageId) -> Result<Self> {
let member_manifest = |package_id: &PackageId| -> Result<(PackageId, (Package, Manifest))> {
let package = metadata
.packages
.iter()
.find(|p| p.id == *package_id)
.unwrap_or_else(|| {
panic!(
"Package '{}' is a member and should be in the packages list",
package_id
)
});
let manifest = Manifest::new(&package.manifest_path)?;
Ok((package_id.clone(), (package.clone(), manifest)))
};
let members = metadata
.workspace_members
.iter()
.map(member_manifest)
.collect::<Result<HashMap<_, _>>>()?;
if !members.contains_key(root_package) {
anyhow::bail!("The root package should be a workspace member")
}
Ok(Workspace {
workspace_root: metadata.workspace_root.clone(),
root_package: root_package.clone(),
members,
})
}
pub fn with_root_package_manifest<F>(&mut self, f: F) -> Result<&mut Self>
where
F: FnOnce(&mut Manifest) -> Result<()>,
{
let root_package_manifest = self
.members
.get_mut(&self.root_package)
.map(|(_, m)| m)
.expect("The root package should be a workspace member");
f(root_package_manifest)?;
Ok(self)
}
pub fn with_contract_manifest<F>(&mut self, package_path: &Path, f: F) -> Result<&mut Self>
where
F: FnOnce(&mut Manifest) -> Result<()>,
{
let manifest = self
.members
.iter_mut()
.find_map(|(_, (_, manifest))| {
if manifest.path().directory() == Some(package_path) {
Some(manifest)
} else {
None
}
})
.ok_or_else(|| {
anyhow::anyhow!("The workspace root package should be a workspace member")
})?;
f(manifest)?;
Ok(self)
}
pub(super) fn with_metadata_gen_package(&mut self, package_path: PathBuf) -> Result<&mut Self> {
self.with_contract_manifest(&package_path, |manifest| {
manifest.with_metadata_package()?;
Ok(())
})
}
pub fn write<P: AsRef<Path>>(&mut self, target: P) -> Result<Vec<(PackageId, ManifestPath)>> {
let exclude_member_package_names = self
.members
.iter()
.map(|(_, (p, _))| p.name.clone())
.collect::<Vec<_>>();
let mut new_manifest_paths = Vec::new();
for (package_id, (package, manifest)) in self.members.iter_mut() {
let mut new_path: PathBuf = target.as_ref().into();
new_path.push(package.manifest_path.strip_prefix(&self.workspace_root)?);
let new_manifest = ManifestPath::new(new_path)?;
manifest.rewrite_relative_paths(&exclude_member_package_names)?;
manifest.write(&new_manifest)?;
new_manifest_paths.push((package_id.clone(), new_manifest));
}
Ok(new_manifest_paths)
}
pub fn using_temp<F>(&mut self, f: F) -> Result<()>
where
F: FnOnce(&ManifestPath) -> Result<()>,
{
let tmp_dir = tempfile::Builder::new()
.prefix("cargo-contract_")
.tempdir()?;
log::debug!("Using temp workspace at '{}'", tmp_dir.path().display());
let new_paths = self.write(&tmp_dir)?;
let root_manifest_path = new_paths
.iter()
.find_map(|(pid, path)| {
if *pid == self.root_package {
Some(path)
} else {
None
}
})
.expect("root package should be a member of the temp workspace");
f(root_manifest_path)
}
}