Skip to main content

foundry_compilers/compile/output/
contracts.rs

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