ethers_solc/compile/output/
contracts.rs1use crate::{
2 artifacts::{
3 contract::{CompactContractRef, Contract},
4 CompactContractBytecode, FileToContractsMap,
5 },
6 files::{MappedArtifactFile, MappedArtifactFiles, MappedContract},
7 ArtifactId, ArtifactOutput, OutputContext,
8};
9use semver::Version;
10use serde::{Deserialize, Deserializer, Serialize, Serializer};
11use std::{
12 collections::BTreeMap,
13 ops::{Deref, DerefMut},
14 path::Path,
15};
16use tracing::trace;
17
18#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
20#[serde(transparent)]
21pub struct VersionedContracts(pub FileToContractsMap<Vec<VersionedContract>>);
22
23impl VersionedContracts {
24 pub fn slash_paths(&mut self) {
26 #[cfg(windows)]
27 {
28 use path_slash::PathExt;
29 self.0 = std::mem::take(&mut self.0)
30 .into_iter()
31 .map(|(path, files)| (Path::new(&path).to_slash_lossy().to_string(), files))
32 .collect()
33 }
34 }
35 pub fn is_empty(&self) -> bool {
36 self.0.is_empty()
37 }
38
39 pub fn len(&self) -> usize {
40 self.0.len()
41 }
42
43 pub fn files(&self) -> impl Iterator<Item = &String> + '_ {
45 self.0.keys()
46 }
47
48 pub(crate) fn artifact_files<T: ArtifactOutput + ?Sized>(
53 &self,
54 ctx: &OutputContext,
55 ) -> MappedArtifactFiles {
56 let mut output_files = MappedArtifactFiles::with_capacity(self.len());
57 for (file, contracts) in self.iter() {
58 for (name, versioned_contracts) in contracts {
59 for contract in versioned_contracts {
60 let artifact_path = if let Some(existing_artifact) =
65 ctx.existing_artifact(file, name, &contract.version).cloned()
66 {
67 trace!("use existing artifact file {:?}", existing_artifact,);
68 existing_artifact
69 } else if versioned_contracts.len() > 1 {
70 T::output_file_versioned(file, name, &contract.version)
71 } else {
72 T::output_file(file, name)
73 };
74
75 trace!(
76 "use artifact file {:?} for contract file {} {}",
77 artifact_path,
78 file,
79 contract.version
80 );
81 let artifact = MappedArtifactFile::new(&artifact_path);
82 let contract = MappedContract {
83 file: file.as_str(),
84 name: name.as_str(),
85 contract,
86 artifact_path,
87 };
88 output_files.entry(artifact).or_default().push(contract);
89 }
90 }
91 }
92
93 output_files
94 }
95
96 pub fn find_first(&self, contract: impl AsRef<str>) -> Option<CompactContractRef> {
109 let contract_name = contract.as_ref();
110 self.contracts().find_map(|(name, contract)| {
111 (name == contract_name).then(|| CompactContractRef::from(contract))
112 })
113 }
114
115 pub fn find(
128 &self,
129 path: impl AsRef<str>,
130 contract: impl AsRef<str>,
131 ) -> Option<CompactContractRef> {
132 let contract_path = path.as_ref();
133 let contract_name = contract.as_ref();
134 self.contracts_with_files().find_map(|(path, name, contract)| {
135 (path == contract_path && name == contract_name)
136 .then(|| CompactContractRef::from(contract))
137 })
138 }
139
140 pub fn remove_first(&mut self, contract: impl AsRef<str>) -> Option<Contract> {
153 let contract_name = contract.as_ref();
154 self.0.values_mut().find_map(|all_contracts| {
155 let mut contract = None;
156 if let Some((c, mut contracts)) = all_contracts.remove_entry(contract_name) {
157 if !contracts.is_empty() {
158 contract = Some(contracts.remove(0).contract);
159 }
160 if !contracts.is_empty() {
161 all_contracts.insert(c, contracts);
162 }
163 }
164 contract
165 })
166 }
167
168 pub fn remove(&mut self, path: impl AsRef<str>, contract: impl AsRef<str>) -> Option<Contract> {
181 let contract_name = contract.as_ref();
182 let (key, mut all_contracts) = self.0.remove_entry(path.as_ref())?;
183 let mut contract = None;
184 if let Some((c, mut contracts)) = all_contracts.remove_entry(contract_name) {
185 if !contracts.is_empty() {
186 contract = Some(contracts.remove(0).contract);
187 }
188 if !contracts.is_empty() {
189 all_contracts.insert(c, contracts);
190 }
191 }
192
193 if !all_contracts.is_empty() {
194 self.0.insert(key, all_contracts);
195 }
196 contract
197 }
198
199 pub fn get(
202 &self,
203 path: impl AsRef<str>,
204 contract: impl AsRef<str>,
205 ) -> Option<CompactContractRef> {
206 let contract = contract.as_ref();
207 self.0
208 .get(path.as_ref())
209 .and_then(|contracts| {
210 contracts.get(contract).and_then(|c| c.first().map(|c| &c.contract))
211 })
212 .map(CompactContractRef::from)
213 }
214
215 pub fn contracts(&self) -> impl Iterator<Item = (&String, &Contract)> {
217 self.0
218 .values()
219 .flat_map(|c| c.iter().flat_map(|(name, c)| c.iter().map(move |c| (name, &c.contract))))
220 }
221
222 pub fn contracts_with_files(&self) -> impl Iterator<Item = (&String, &String, &Contract)> {
224 self.0.iter().flat_map(|(file, contracts)| {
225 contracts
226 .iter()
227 .flat_map(move |(name, c)| c.iter().map(move |c| (file, name, &c.contract)))
228 })
229 }
230
231 pub fn contracts_with_files_and_version(
233 &self,
234 ) -> impl Iterator<Item = (&String, &String, &Contract, &Version)> {
235 self.0.iter().flat_map(|(file, contracts)| {
236 contracts.iter().flat_map(move |(name, c)| {
237 c.iter().map(move |c| (file, name, &c.contract, &c.version))
238 })
239 })
240 }
241
242 pub fn into_contracts(self) -> impl Iterator<Item = (String, Contract)> {
256 self.0.into_values().flat_map(|c| {
257 c.into_iter()
258 .flat_map(|(name, c)| c.into_iter().map(move |c| (name.clone(), c.contract)))
259 })
260 }
261
262 pub fn into_contracts_with_files(self) -> impl Iterator<Item = (String, String, Contract)> {
264 self.0.into_iter().flat_map(|(file, contracts)| {
265 contracts.into_iter().flat_map(move |(name, c)| {
266 let file = file.clone();
267 c.into_iter().map(move |c| (file.clone(), name.clone(), c.contract))
268 })
269 })
270 }
271
272 pub fn into_contracts_with_files_and_version(
274 self,
275 ) -> impl Iterator<Item = (String, String, Contract, Version)> {
276 self.0.into_iter().flat_map(|(file, contracts)| {
277 contracts.into_iter().flat_map(move |(name, c)| {
278 let file = file.clone();
279 c.into_iter().map(move |c| (file.clone(), name.clone(), c.contract, c.version))
280 })
281 })
282 }
283
284 pub fn join_all(&mut self, root: impl AsRef<Path>) -> &mut Self {
286 let root = root.as_ref();
287 self.0 = std::mem::take(&mut self.0)
288 .into_iter()
289 .map(|(contract_path, contracts)| {
290 (format!("{}", root.join(contract_path).display()), contracts)
291 })
292 .collect();
293 self
294 }
295
296 pub fn strip_prefix_all(&mut self, base: impl AsRef<Path>) -> &mut Self {
298 let base = base.as_ref();
299 self.0 = std::mem::take(&mut self.0)
300 .into_iter()
301 .map(|(contract_path, contracts)| {
302 let p = Path::new(&contract_path);
303 (
304 p.strip_prefix(base)
305 .map(|p| p.to_string_lossy().to_string())
306 .unwrap_or(contract_path),
307 contracts,
308 )
309 })
310 .collect();
311 self
312 }
313}
314
315impl AsRef<FileToContractsMap<Vec<VersionedContract>>> for VersionedContracts {
316 fn as_ref(&self) -> &FileToContractsMap<Vec<VersionedContract>> {
317 &self.0
318 }
319}
320
321impl AsMut<FileToContractsMap<Vec<VersionedContract>>> for VersionedContracts {
322 fn as_mut(&mut self) -> &mut FileToContractsMap<Vec<VersionedContract>> {
323 &mut self.0
324 }
325}
326
327impl Deref for VersionedContracts {
328 type Target = FileToContractsMap<Vec<VersionedContract>>;
329
330 fn deref(&self) -> &Self::Target {
331 &self.0
332 }
333}
334
335impl IntoIterator for VersionedContracts {
336 type Item = (String, BTreeMap<String, Vec<VersionedContract>>);
337 type IntoIter =
338 std::collections::btree_map::IntoIter<String, BTreeMap<String, Vec<VersionedContract>>>;
339
340 fn into_iter(self) -> Self::IntoIter {
341 self.0.into_iter()
342 }
343}
344
345#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
347pub struct VersionedContract {
348 pub contract: Contract,
349 pub version: Version,
350}
351
352#[derive(Debug, Clone, PartialEq, Eq, Default)]
354pub struct ArtifactContracts<T = CompactContractBytecode>(pub BTreeMap<ArtifactId, T>);
355
356impl<T: Serialize> Serialize for ArtifactContracts<T> {
357 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
358 where
359 S: Serializer,
360 {
361 self.0.serialize(serializer)
362 }
363}
364
365impl<'de, T: Deserialize<'de>> Deserialize<'de> for ArtifactContracts<T> {
366 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
367 where
368 D: Deserializer<'de>,
369 {
370 Ok(Self(BTreeMap::<_, _>::deserialize(deserializer)?))
371 }
372}
373
374impl<T> Deref for ArtifactContracts<T> {
375 type Target = BTreeMap<ArtifactId, T>;
376
377 fn deref(&self) -> &Self::Target {
378 &self.0
379 }
380}
381
382impl<T> DerefMut for ArtifactContracts<T> {
383 fn deref_mut(&mut self) -> &mut Self::Target {
384 &mut self.0
385 }
386}
387
388impl<V, C: Into<V>> FromIterator<(ArtifactId, C)> for ArtifactContracts<V> {
389 fn from_iter<T: IntoIterator<Item = (ArtifactId, C)>>(iter: T) -> Self {
390 Self(iter.into_iter().map(|(k, v)| (k, v.into())).collect())
391 }
392}
393
394impl<T> IntoIterator for ArtifactContracts<T> {
395 type Item = (ArtifactId, T);
396 type IntoIter = std::collections::btree_map::IntoIter<ArtifactId, T>;
397
398 fn into_iter(self) -> Self::IntoIter {
399 self.0.into_iter()
400 }
401}