foundry_compilers/compile/output/
contracts.rs

1use crate::{compilers::CompilerContract, ArtifactId};
2use foundry_compilers_artifacts::{
3    CompactContractBytecode, CompactContractRef, FileToContractsMap,
4};
5use semver::Version;
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use std::{
8    collections::BTreeMap,
9    ops::{Deref, DerefMut},
10    path::{Path, PathBuf},
11};
12
13/// file -> [(contract name  -> Contract + solc version)]
14#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(transparent)]
16pub struct VersionedContracts<C>(pub FileToContractsMap<Vec<VersionedContract<C>>>);
17
18impl<C> Default for VersionedContracts<C> {
19    fn default() -> Self {
20        Self(BTreeMap::new())
21    }
22}
23
24impl<C> VersionedContracts<C>
25where
26    C: CompilerContract,
27{
28    /// Converts all `\\` separators in _all_ paths to `/`
29    pub fn slash_paths(&mut self) {
30        #[cfg(windows)]
31        {
32            use path_slash::PathExt;
33            self.0 = std::mem::take(&mut self.0)
34                .into_iter()
35                .map(|(path, files)| (PathBuf::from(path.to_slash_lossy().as_ref()), files))
36                .collect()
37        }
38    }
39    pub fn is_empty(&self) -> bool {
40        self.0.is_empty()
41    }
42
43    pub fn len(&self) -> usize {
44        self.0.len()
45    }
46
47    /// Returns an iterator over all files
48    pub fn files(&self) -> impl Iterator<Item = &PathBuf> + '_ {
49        self.0.keys()
50    }
51
52    /// Finds the _first_ contract with the given name
53    ///
54    /// # Examples
55    /// ```no_run
56    /// use foundry_compilers::{artifacts::*, Project};
57    ///
58    /// let project = Project::builder().build(Default::default())?;
59    /// let output = project.compile()?.into_output();
60    /// let contract = output.find_first("Greeter").unwrap();
61    /// # Ok::<_, Box<dyn std::error::Error>>(())
62    /// ```
63    pub fn find_first(&self, contract_name: &str) -> Option<CompactContractRef<'_>> {
64        self.contracts().find_map(|(name, contract)| {
65            (name == contract_name).then(|| contract.as_compact_contract_ref())
66        })
67    }
68
69    /// Finds the contract with matching path and name
70    ///
71    /// # Examples
72    /// ```no_run
73    /// use foundry_compilers::{artifacts::*, Project};
74    ///
75    /// let project = Project::builder().build(Default::default())?;
76    /// let output = project.compile()?.into_output();
77    /// let contract = output.contracts.find("src/Greeter.sol".as_ref(), "Greeter").unwrap();
78    /// # Ok::<_, Box<dyn std::error::Error>>(())
79    /// ```
80    pub fn find(
81        &self,
82        contract_path: &Path,
83        contract_name: &str,
84    ) -> Option<CompactContractRef<'_>> {
85        self.contracts_with_files().find_map(|(path, name, contract)| {
86            (path == contract_path && name == contract_name)
87                .then(|| contract.as_compact_contract_ref())
88        })
89    }
90
91    /// Removes the _first_ contract with the given name from the set
92    ///
93    /// # Examples
94    /// ```no_run
95    /// use foundry_compilers::{artifacts::*, Project};
96    ///
97    /// let project = Project::builder().build(Default::default())?;
98    /// let (_, mut contracts) = project.compile()?.into_output().split();
99    /// let contract = contracts.remove_first("Greeter").unwrap();
100    /// # Ok::<_, Box<dyn std::error::Error>>(())
101    /// ```
102    pub fn remove_first(&mut self, contract_name: &str) -> Option<C> {
103        self.0.values_mut().find_map(|all_contracts| {
104            let mut contract = None;
105            if let Some((c, mut contracts)) = all_contracts.remove_entry(contract_name) {
106                if !contracts.is_empty() {
107                    contract = Some(contracts.remove(0).contract);
108                }
109                if !contracts.is_empty() {
110                    all_contracts.insert(c, contracts);
111                }
112            }
113            contract
114        })
115    }
116
117    ///  Removes the contract with matching path and name
118    ///
119    /// # Examples
120    /// ```no_run
121    /// use foundry_compilers::{artifacts::*, Project};
122    ///
123    /// let project = Project::builder().build(Default::default())?;
124    /// let (_, mut contracts) = project.compile()?.into_output().split();
125    /// let contract = contracts.remove("src/Greeter.sol".as_ref(), "Greeter").unwrap();
126    /// # Ok::<_, Box<dyn std::error::Error>>(())
127    /// ```
128    pub fn remove(&mut self, path: &Path, contract_name: &str) -> Option<C> {
129        let (key, mut all_contracts) = self.0.remove_entry(path)?;
130        let mut contract = None;
131        if let Some((c, mut contracts)) = all_contracts.remove_entry(contract_name) {
132            if !contracts.is_empty() {
133                contract = Some(contracts.remove(0).contract);
134            }
135            if !contracts.is_empty() {
136                all_contracts.insert(c, contracts);
137            }
138        }
139
140        if !all_contracts.is_empty() {
141            self.0.insert(key, all_contracts);
142        }
143        contract
144    }
145
146    /// Given the contract file's path and the contract's name, tries to return the contract's
147    /// bytecode, runtime bytecode, and ABI.
148    pub fn get(&self, path: &Path, contract: &str) -> Option<CompactContractRef<'_>> {
149        self.0
150            .get(path)
151            .and_then(|contracts| {
152                contracts.get(contract).and_then(|c| c.first().map(|c| &c.contract))
153            })
154            .map(|c| c.as_compact_contract_ref())
155    }
156
157    /// Returns an iterator over all contracts and their names.
158    pub fn contracts(&self) -> impl Iterator<Item = (&String, &C)> {
159        self.0
160            .values()
161            .flat_map(|c| c.iter().flat_map(|(name, c)| c.iter().map(move |c| (name, &c.contract))))
162    }
163
164    /// Returns an iterator over (`file`, `name`, `Contract`).
165    pub fn contracts_with_files(&self) -> impl Iterator<Item = (&PathBuf, &String, &C)> {
166        self.0.iter().flat_map(|(file, contracts)| {
167            contracts
168                .iter()
169                .flat_map(move |(name, c)| c.iter().map(move |c| (file, name, &c.contract)))
170        })
171    }
172
173    /// Returns an iterator over (`file`, `name`, `Contract`, `Version`).
174    pub fn contracts_with_files_and_version(
175        &self,
176    ) -> impl Iterator<Item = (&PathBuf, &String, &C, &Version)> {
177        self.0.iter().flat_map(|(file, contracts)| {
178            contracts.iter().flat_map(move |(name, c)| {
179                c.iter().map(move |c| (file, name, &c.contract, &c.version))
180            })
181        })
182    }
183
184    /// Returns an iterator over all contracts and their source names.
185    pub fn into_contracts(self) -> impl Iterator<Item = (String, C)> {
186        self.0.into_values().flat_map(|c| {
187            c.into_iter()
188                .flat_map(|(name, c)| c.into_iter().map(move |c| (name.clone(), c.contract)))
189        })
190    }
191
192    /// Returns an iterator over (`file`, `name`, `Contract`)
193    pub fn into_contracts_with_files(self) -> impl Iterator<Item = (PathBuf, String, C)> {
194        self.0.into_iter().flat_map(|(file, contracts)| {
195            contracts.into_iter().flat_map(move |(name, c)| {
196                let file = file.clone();
197                c.into_iter().map(move |c| (file.clone(), name.clone(), c.contract))
198            })
199        })
200    }
201
202    /// Returns an iterator over (`file`, `name`, `Contract`, `Version`)
203    pub fn into_contracts_with_files_and_version(
204        self,
205    ) -> impl Iterator<Item = (PathBuf, String, C, Version)> {
206        self.0.into_iter().flat_map(|(file, contracts)| {
207            contracts.into_iter().flat_map(move |(name, c)| {
208                let file = file.clone();
209                c.into_iter().map(move |c| (file.clone(), name.clone(), c.contract, c.version))
210            })
211        })
212    }
213
214    /// Sets the contract's file paths to `root` adjoined to `self.file`.
215    pub fn join_all(&mut self, root: &Path) -> &mut Self {
216        self.0 = std::mem::take(&mut self.0)
217            .into_iter()
218            .map(|(contract_path, contracts)| (root.join(contract_path), contracts))
219            .collect();
220        self
221    }
222
223    /// Removes `base` from all contract paths
224    pub fn strip_prefix_all(&mut self, base: &Path) -> &mut Self {
225        self.0 = std::mem::take(&mut self.0)
226            .into_iter()
227            .map(|(contract_path, contracts)| {
228                (
229                    contract_path.strip_prefix(base).unwrap_or(&contract_path).to_path_buf(),
230                    contracts,
231                )
232            })
233            .collect();
234        self
235    }
236}
237
238impl<C> AsRef<FileToContractsMap<Vec<VersionedContract<C>>>> for VersionedContracts<C>
239where
240    C: CompilerContract,
241{
242    fn as_ref(&self) -> &FileToContractsMap<Vec<VersionedContract<C>>> {
243        &self.0
244    }
245}
246
247impl<C> AsMut<FileToContractsMap<Vec<VersionedContract<C>>>> for VersionedContracts<C>
248where
249    C: CompilerContract,
250{
251    fn as_mut(&mut self) -> &mut FileToContractsMap<Vec<VersionedContract<C>>> {
252        &mut self.0
253    }
254}
255
256impl<C> Deref for VersionedContracts<C>
257where
258    C: CompilerContract,
259{
260    type Target = FileToContractsMap<Vec<VersionedContract<C>>>;
261
262    fn deref(&self) -> &Self::Target {
263        &self.0
264    }
265}
266
267impl<C> IntoIterator for VersionedContracts<C>
268where
269    C: CompilerContract,
270{
271    type Item = (PathBuf, BTreeMap<String, Vec<VersionedContract<C>>>);
272    type IntoIter =
273        std::collections::btree_map::IntoIter<PathBuf, BTreeMap<String, Vec<VersionedContract<C>>>>;
274
275    fn into_iter(self) -> Self::IntoIter {
276        self.0.into_iter()
277    }
278}
279
280/// A contract and the compiler version used to compile it
281#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
282pub struct VersionedContract<C> {
283    pub contract: C,
284    pub version: Version,
285    pub build_id: String,
286    pub profile: String,
287}
288
289/// A mapping of `ArtifactId` and their `CompactContractBytecode`
290#[derive(Clone, Debug, Default, PartialEq, Eq)]
291pub struct ArtifactContracts<T = CompactContractBytecode>(pub BTreeMap<ArtifactId, T>);
292
293impl<T: Serialize> Serialize for ArtifactContracts<T> {
294    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
295    where
296        S: Serializer,
297    {
298        self.0.serialize(serializer)
299    }
300}
301
302impl<'de, T: Deserialize<'de>> Deserialize<'de> for ArtifactContracts<T> {
303    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
304    where
305        D: Deserializer<'de>,
306    {
307        Ok(Self(BTreeMap::<_, _>::deserialize(deserializer)?))
308    }
309}
310
311impl<T> Deref for ArtifactContracts<T> {
312    type Target = BTreeMap<ArtifactId, T>;
313
314    fn deref(&self) -> &Self::Target {
315        &self.0
316    }
317}
318
319impl<T> DerefMut for ArtifactContracts<T> {
320    fn deref_mut(&mut self) -> &mut Self::Target {
321        &mut self.0
322    }
323}
324
325impl<V, C: Into<V>> FromIterator<(ArtifactId, C)> for ArtifactContracts<V> {
326    fn from_iter<T: IntoIterator<Item = (ArtifactId, C)>>(iter: T) -> Self {
327        Self(iter.into_iter().map(|(k, v)| (k, v.into())).collect())
328    }
329}
330
331impl<T> IntoIterator for ArtifactContracts<T> {
332    type Item = (ArtifactId, T);
333    type IntoIter = std::collections::btree_map::IntoIter<ArtifactId, T>;
334
335    fn into_iter(self) -> Self::IntoIter {
336        self.0.into_iter()
337    }
338}