use crate::{
artifacts::{
contract::{CompactContractRef, Contract},
CompactContractBytecode, FileToContractsMap,
},
files::{MappedArtifactFile, MappedArtifactFiles, MappedContract},
ArtifactId, ArtifactOutput, OutputContext,
};
use semver::Version;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{
collections::BTreeMap,
ops::{Deref, DerefMut},
path::Path,
};
use tracing::trace;
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(transparent)]
pub struct VersionedContracts(pub FileToContractsMap<Vec<VersionedContract>>);
impl VersionedContracts {
pub fn slash_paths(&mut self) {
#[cfg(windows)]
{
use path_slash::PathExt;
self.0 = std::mem::take(&mut self.0)
.into_iter()
.map(|(path, files)| (Path::new(&path).to_slash_lossy().to_string(), files))
.collect()
}
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn files(&self) -> impl Iterator<Item = &String> + '_ {
self.0.keys()
}
pub(crate) fn artifact_files<T: ArtifactOutput + ?Sized>(
&self,
ctx: &OutputContext,
) -> MappedArtifactFiles {
let mut output_files = MappedArtifactFiles::with_capacity(self.len());
for (file, contracts) in self.iter() {
for (name, versioned_contracts) in contracts {
for contract in versioned_contracts {
let artifact_path = if let Some(existing_artifact) =
ctx.existing_artifact(file, name, &contract.version).cloned()
{
trace!("use existing artifact file {:?}", existing_artifact,);
existing_artifact
} else if versioned_contracts.len() > 1 {
T::output_file_versioned(file, name, &contract.version)
} else {
T::output_file(file, name)
};
trace!(
"use artifact file {:?} for contract file {} {}",
artifact_path,
file,
contract.version
);
let artifact = MappedArtifactFile::new(&artifact_path);
let contract = MappedContract {
file: file.as_str(),
name: name.as_str(),
contract,
artifact_path,
};
output_files.entry(artifact).or_default().push(contract);
}
}
}
output_files
}
pub fn find_first(&self, contract: impl AsRef<str>) -> Option<CompactContractRef> {
let contract_name = contract.as_ref();
self.contracts().find_map(|(name, contract)| {
(name == contract_name).then(|| CompactContractRef::from(contract))
})
}
pub fn find(
&self,
path: impl AsRef<str>,
contract: impl AsRef<str>,
) -> Option<CompactContractRef> {
let contract_path = path.as_ref();
let contract_name = contract.as_ref();
self.contracts_with_files().find_map(|(path, name, contract)| {
(path == contract_path && name == contract_name)
.then(|| CompactContractRef::from(contract))
})
}
pub fn remove_first(&mut self, contract: impl AsRef<str>) -> Option<Contract> {
let contract_name = contract.as_ref();
self.0.values_mut().find_map(|all_contracts| {
let mut contract = None;
if let Some((c, mut contracts)) = all_contracts.remove_entry(contract_name) {
if !contracts.is_empty() {
contract = Some(contracts.remove(0).contract);
}
if !contracts.is_empty() {
all_contracts.insert(c, contracts);
}
}
contract
})
}
pub fn remove(&mut self, path: impl AsRef<str>, contract: impl AsRef<str>) -> Option<Contract> {
let contract_name = contract.as_ref();
let (key, mut all_contracts) = self.0.remove_entry(path.as_ref())?;
let mut contract = None;
if let Some((c, mut contracts)) = all_contracts.remove_entry(contract_name) {
if !contracts.is_empty() {
contract = Some(contracts.remove(0).contract);
}
if !contracts.is_empty() {
all_contracts.insert(c, contracts);
}
}
if !all_contracts.is_empty() {
self.0.insert(key, all_contracts);
}
contract
}
pub fn get(
&self,
path: impl AsRef<str>,
contract: impl AsRef<str>,
) -> Option<CompactContractRef> {
let contract = contract.as_ref();
self.0
.get(path.as_ref())
.and_then(|contracts| {
contracts.get(contract).and_then(|c| c.first().map(|c| &c.contract))
})
.map(CompactContractRef::from)
}
pub fn contracts(&self) -> impl Iterator<Item = (&String, &Contract)> {
self.0
.values()
.flat_map(|c| c.iter().flat_map(|(name, c)| c.iter().map(move |c| (name, &c.contract))))
}
pub fn contracts_with_files(&self) -> impl Iterator<Item = (&String, &String, &Contract)> {
self.0.iter().flat_map(|(file, contracts)| {
contracts
.iter()
.flat_map(move |(name, c)| c.iter().map(move |c| (file, name, &c.contract)))
})
}
pub fn contracts_with_files_and_version(
&self,
) -> impl Iterator<Item = (&String, &String, &Contract, &Version)> {
self.0.iter().flat_map(|(file, contracts)| {
contracts.iter().flat_map(move |(name, c)| {
c.iter().map(move |c| (file, name, &c.contract, &c.version))
})
})
}
pub fn into_contracts(self) -> impl Iterator<Item = (String, Contract)> {
self.0.into_values().flat_map(|c| {
c.into_iter()
.flat_map(|(name, c)| c.into_iter().map(move |c| (name.clone(), c.contract)))
})
}
pub fn into_contracts_with_files(self) -> impl Iterator<Item = (String, String, Contract)> {
self.0.into_iter().flat_map(|(file, contracts)| {
contracts.into_iter().flat_map(move |(name, c)| {
let file = file.clone();
c.into_iter().map(move |c| (file.clone(), name.clone(), c.contract))
})
})
}
pub fn into_contracts_with_files_and_version(
self,
) -> impl Iterator<Item = (String, String, Contract, Version)> {
self.0.into_iter().flat_map(|(file, contracts)| {
contracts.into_iter().flat_map(move |(name, c)| {
let file = file.clone();
c.into_iter().map(move |c| (file.clone(), name.clone(), c.contract, c.version))
})
})
}
pub fn join_all(&mut self, root: impl AsRef<Path>) -> &mut Self {
let root = root.as_ref();
self.0 = std::mem::take(&mut self.0)
.into_iter()
.map(|(contract_path, contracts)| {
(format!("{}", root.join(contract_path).display()), contracts)
})
.collect();
self
}
pub fn strip_prefix_all(&mut self, base: impl AsRef<Path>) -> &mut Self {
let base = base.as_ref();
self.0 = std::mem::take(&mut self.0)
.into_iter()
.map(|(contract_path, contracts)| {
let p = Path::new(&contract_path);
(
p.strip_prefix(base)
.map(|p| p.to_string_lossy().to_string())
.unwrap_or(contract_path),
contracts,
)
})
.collect();
self
}
}
impl AsRef<FileToContractsMap<Vec<VersionedContract>>> for VersionedContracts {
fn as_ref(&self) -> &FileToContractsMap<Vec<VersionedContract>> {
&self.0
}
}
impl AsMut<FileToContractsMap<Vec<VersionedContract>>> for VersionedContracts {
fn as_mut(&mut self) -> &mut FileToContractsMap<Vec<VersionedContract>> {
&mut self.0
}
}
impl Deref for VersionedContracts {
type Target = FileToContractsMap<Vec<VersionedContract>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl IntoIterator for VersionedContracts {
type Item = (String, BTreeMap<String, Vec<VersionedContract>>);
type IntoIter =
std::collections::btree_map::IntoIter<String, BTreeMap<String, Vec<VersionedContract>>>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct VersionedContract {
pub contract: Contract,
pub version: Version,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ArtifactContracts<T = CompactContractBytecode>(pub BTreeMap<ArtifactId, T>);
impl<T: Serialize> Serialize for ArtifactContracts<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
impl<'de, T: Deserialize<'de>> Deserialize<'de> for ArtifactContracts<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self(BTreeMap::<_, _>::deserialize(deserializer)?))
}
}
impl<T> Deref for ArtifactContracts<T> {
type Target = BTreeMap<ArtifactId, T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for ArtifactContracts<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<V, C: Into<V>> FromIterator<(ArtifactId, C)> for ArtifactContracts<V> {
fn from_iter<T: IntoIterator<Item = (ArtifactId, C)>>(iter: T) -> Self {
Self(iter.into_iter().map(|(k, v)| (k, v.into())).collect())
}
}
impl<T> IntoIterator for ArtifactContracts<T> {
type Item = (ArtifactId, T);
type IntoIter = std::collections::btree_map::IntoIter<ArtifactId, T>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}