use crate::ArtifactId;
use foundry_compilers_artifacts::{
CompactContractBytecode, CompactContractRef, Contract, FileToContractsMap,
};
use semver::Version;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{
collections::BTreeMap,
ops::{Deref, DerefMut},
path::{Path, PathBuf},
};
#[derive(Clone, Debug, Default, PartialEq, Eq, 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)| (PathBuf::from(path.to_slash_lossy().as_ref()), 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 = &PathBuf> + '_ {
self.0.keys()
}
#[cfg_attr(not(feature = "svm-solc"), doc = "```ignore")]
pub fn find_first(&self, contract_name: &str) -> Option<CompactContractRef<'_>> {
self.contracts().find_map(|(name, contract)| {
(name == contract_name).then(|| CompactContractRef::from(contract))
})
}
#[cfg_attr(not(feature = "svm-solc"), doc = "```ignore")]
pub fn find(
&self,
contract_path: &Path,
contract_name: &str,
) -> Option<CompactContractRef<'_>> {
self.contracts_with_files().find_map(|(path, name, contract)| {
(path == contract_path && name == contract_name)
.then(|| CompactContractRef::from(contract))
})
}
#[cfg_attr(not(feature = "svm-solc"), doc = "```ignore")]
pub fn remove_first(&mut self, contract_name: &str) -> Option<Contract> {
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
})
}
#[cfg_attr(not(feature = "svm-solc"), doc = "```ignore")]
pub fn remove(&mut self, path: &Path, contract_name: &str) -> Option<Contract> {
let (key, mut all_contracts) = self.0.remove_entry(path)?;
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: &Path, contract: &str) -> Option<CompactContractRef<'_>> {
self.0
.get(path)
.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 = (&PathBuf, &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 = (&PathBuf, &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 = (PathBuf, 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 = (PathBuf, 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: &Path) -> &mut Self {
self.0 = std::mem::take(&mut self.0)
.into_iter()
.map(|(contract_path, contracts)| (root.join(contract_path), contracts))
.collect();
self
}
pub fn strip_prefix_all(&mut self, base: &Path) -> &mut Self {
self.0 = std::mem::take(&mut self.0)
.into_iter()
.map(|(contract_path, contracts)| {
(
contract_path.strip_prefix(base).unwrap_or(&contract_path).to_path_buf(),
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 = (PathBuf, BTreeMap<String, Vec<VersionedContract>>);
type IntoIter =
std::collections::btree_map::IntoIter<PathBuf, BTreeMap<String, Vec<VersionedContract>>>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct VersionedContract {
pub contract: Contract,
pub version: Version,
pub build_id: String,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
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()
}
}