ethers_solc/compile/output/
sources.rs

1use crate::SourceFile;
2use semver::Version;
3use serde::{Deserialize, Serialize};
4use std::{collections::BTreeMap, path::Path};
5
6/// (source_file path  -> `SourceFile` + solc version)
7#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
8#[serde(transparent)]
9pub struct VersionedSourceFiles(pub BTreeMap<String, Vec<VersionedSourceFile>>);
10
11impl VersionedSourceFiles {
12    /// Converts all `\\` separators in _all_ paths to `/`
13    pub fn slash_paths(&mut self) {
14        #[cfg(windows)]
15        {
16            use path_slash::PathExt;
17            self.0 = std::mem::take(&mut self.0)
18                .into_iter()
19                .map(|(path, files)| (Path::new(&path).to_slash_lossy().to_string(), files))
20                .collect()
21        }
22    }
23
24    pub fn is_empty(&self) -> bool {
25        self.0.is_empty()
26    }
27
28    pub fn len(&self) -> usize {
29        self.0.len()
30    }
31
32    /// Returns an iterator over all files
33    pub fn files(&self) -> impl Iterator<Item = &String> + '_ {
34        self.0.keys()
35    }
36
37    /// Returns an iterator over the source files' ids and path
38    ///
39    /// ```
40    /// use std::collections::BTreeMap;
41    /// use ethers_solc::sources::VersionedSourceFiles;
42    /// # fn demo(files: VersionedSourceFiles) {
43    /// let sources: BTreeMap<u32,String> = files.into_ids().collect();
44    /// # }
45    /// ```
46    pub fn into_ids(self) -> impl Iterator<Item = (u32, String)> {
47        self.into_sources().map(|(path, source)| (source.id, path))
48    }
49
50    /// Returns an iterator over the source files' paths and ids
51    ///
52    /// ```
53    /// use std::collections::BTreeMap;
54    /// use ethers_solc::artifacts::SourceFiles;
55    /// # fn demo(files: SourceFiles) {
56    /// let sources :BTreeMap<String, u32> = files.into_paths().collect();
57    /// # }
58    /// ```
59    pub fn into_paths(self) -> impl Iterator<Item = (String, u32)> {
60        self.into_ids().map(|(id, path)| (path, id))
61    }
62
63    /// Returns an iterator over the source files' ids and path
64    ///
65    /// ```
66    /// use std::collections::BTreeMap;
67    /// use semver::Version;
68    /// use ethers_solc::sources::VersionedSourceFiles;
69    /// # fn demo(files: VersionedSourceFiles) {
70    /// let sources: BTreeMap<(u32, Version) ,String> = files.into_ids_with_version().map(|(id, source, version)|((id, version), source)).collect();
71    /// # }
72    /// ```
73    pub fn into_ids_with_version(self) -> impl Iterator<Item = (u32, String, Version)> {
74        self.into_sources_with_version().map(|(path, source, version)| (source.id, path, version))
75    }
76
77    /// Finds the _first_ source file with the given path
78    ///
79    /// # Example
80    ///
81    /// ```
82    /// use ethers_solc::Project;
83    /// use ethers_solc::artifacts::*;
84    /// # fn demo(project: Project) {
85    /// let output = project.compile().unwrap().output();
86    /// let source_file = output.sources.find_file("src/Greeter.sol").unwrap();
87    /// # }
88    /// ```
89    pub fn find_file(&self, source_file: impl AsRef<str>) -> Option<&SourceFile> {
90        let source_file_name = source_file.as_ref();
91        self.sources().find_map(
92            |(path, source_file)| {
93                if path == source_file_name {
94                    Some(source_file)
95                } else {
96                    None
97                }
98            },
99        )
100    }
101
102    /// Same as [Self::find_file] but also checks for version
103    pub fn find_file_and_version(&self, path: &str, version: &Version) -> Option<&SourceFile> {
104        self.0.get(path).and_then(|contracts| {
105            contracts.iter().find_map(|source| {
106                if source.version == *version {
107                    Some(&source.source_file)
108                } else {
109                    None
110                }
111            })
112        })
113    }
114
115    /// Finds the _first_ source file with the given id
116    ///
117    /// # Example
118    ///
119    /// ```
120    /// use ethers_solc::Project;
121    /// use ethers_solc::artifacts::*;
122    /// # fn demo(project: Project) {
123    /// let output = project.compile().unwrap().output();
124    /// let source_file = output.sources.find_id(0).unwrap();
125    /// # }
126    /// ```
127    pub fn find_id(&self, id: u32) -> Option<&SourceFile> {
128        self.sources().filter(|(_, source)| source.id == id).map(|(_, source)| source).next()
129    }
130
131    /// Same as [Self::find_id] but also checks for version
132    pub fn find_id_and_version(&self, id: u32, version: &Version) -> Option<&SourceFile> {
133        self.sources_with_version()
134            .filter(|(_, source, v)| source.id == id && *v == version)
135            .map(|(_, source, _)| source)
136            .next()
137    }
138
139    /// Removes the _first_ source_file with the given path from the set
140    ///
141    /// # Example
142    ///
143    /// ```
144    /// use ethers_solc::Project;
145    /// use ethers_solc::artifacts::*;
146    /// # fn demo(project: Project) {
147    /// let (mut sources, _) = project.compile().unwrap().output().split();
148    /// let source_file = sources.remove_by_path("src/Greeter.sol").unwrap();
149    /// # }
150    /// ```
151    pub fn remove_by_path(&mut self, source_file: impl AsRef<str>) -> Option<SourceFile> {
152        let source_file_path = source_file.as_ref();
153        self.0.get_mut(source_file_path).and_then(|all_sources| {
154            if !all_sources.is_empty() {
155                Some(all_sources.remove(0).source_file)
156            } else {
157                None
158            }
159        })
160    }
161
162    /// Removes the _first_ source_file with the given id from the set
163    ///
164    /// # Example
165    ///
166    /// ```
167    /// use ethers_solc::Project;
168    /// use ethers_solc::artifacts::*;
169    /// # fn demo(project: Project) {
170    /// let (mut sources, _) = project.compile().unwrap().output().split();
171    /// let source_file = sources.remove_by_id(0).unwrap();
172    /// # }
173    /// ```
174    pub fn remove_by_id(&mut self, id: u32) -> Option<SourceFile> {
175        self.0
176            .values_mut()
177            .filter_map(|sources| {
178                sources
179                    .iter()
180                    .position(|source| source.source_file.id == id)
181                    .map(|pos| sources.remove(pos).source_file)
182            })
183            .next()
184    }
185
186    /// Iterate over all contracts and their names
187    pub fn sources(&self) -> impl Iterator<Item = (&String, &SourceFile)> {
188        self.0.iter().flat_map(|(path, sources)| {
189            sources.iter().map(move |source| (path, &source.source_file))
190        })
191    }
192
193    /// Returns an iterator over (`file`,  `SourceFile`, `Version`)
194    pub fn sources_with_version(&self) -> impl Iterator<Item = (&String, &SourceFile, &Version)> {
195        self.0.iter().flat_map(|(file, sources)| {
196            sources.iter().map(move |c| (file, &c.source_file, &c.version))
197        })
198    }
199
200    /// Returns an iterator over all contracts and their source names.
201    ///
202    /// ```
203    /// use std::collections::BTreeMap;
204    /// use ethers_solc::{ artifacts::* };
205    /// use ethers_solc::sources::VersionedSourceFiles;
206    /// # fn demo(sources: VersionedSourceFiles) {
207    /// let sources: BTreeMap<String, SourceFile> = sources
208    ///     .into_sources()
209    ///     .collect();
210    /// # }
211    /// ```
212    pub fn into_sources(self) -> impl Iterator<Item = (String, SourceFile)> {
213        self.0.into_iter().flat_map(|(path, sources)| {
214            sources.into_iter().map(move |source| (path.clone(), source.source_file))
215        })
216    }
217
218    /// Returns an iterator over all contracts and their source names.
219    ///
220    /// ```
221    /// use std::collections::BTreeMap;
222    /// use semver::Version;
223    /// use ethers_solc::{ artifacts::* };
224    /// use ethers_solc::sources::VersionedSourceFiles;
225    /// # fn demo(sources: VersionedSourceFiles) {
226    /// let sources: BTreeMap<(String,Version), SourceFile> = sources
227    ///     .into_sources_with_version().map(|(path, source, version)|((path,version), source))
228    ///     .collect();
229    /// # }
230    /// ```
231    pub fn into_sources_with_version(self) -> impl Iterator<Item = (String, SourceFile, Version)> {
232        self.0.into_iter().flat_map(|(path, sources)| {
233            sources
234                .into_iter()
235                .map(move |source| (path.clone(), source.source_file, source.version))
236        })
237    }
238
239    /// Sets the sources' file paths to `root` adjoined to `self.file`.
240    pub fn join_all(&mut self, root: impl AsRef<Path>) -> &mut Self {
241        let root = root.as_ref();
242        self.0 = std::mem::take(&mut self.0)
243            .into_iter()
244            .map(|(file_path, sources)| {
245                (root.join(file_path).to_string_lossy().to_string(), sources)
246            })
247            .collect();
248        self
249    }
250
251    /// Removes `base` from all source file paths
252    pub fn strip_prefix_all(&mut self, base: impl AsRef<Path>) -> &mut Self {
253        let base = base.as_ref();
254        self.0 = std::mem::take(&mut self.0)
255            .into_iter()
256            .map(|(file_path, sources)| {
257                let p = Path::new(&file_path);
258                (
259                    p.strip_prefix(base)
260                        .map(|p| p.to_string_lossy().to_string())
261                        .unwrap_or(file_path),
262                    sources,
263                )
264            })
265            .collect();
266        self
267    }
268}
269
270impl AsRef<BTreeMap<String, Vec<VersionedSourceFile>>> for VersionedSourceFiles {
271    fn as_ref(&self) -> &BTreeMap<String, Vec<VersionedSourceFile>> {
272        &self.0
273    }
274}
275
276impl AsMut<BTreeMap<String, Vec<VersionedSourceFile>>> for VersionedSourceFiles {
277    fn as_mut(&mut self) -> &mut BTreeMap<String, Vec<VersionedSourceFile>> {
278        &mut self.0
279    }
280}
281
282impl IntoIterator for VersionedSourceFiles {
283    type Item = (String, Vec<VersionedSourceFile>);
284    type IntoIter = std::collections::btree_map::IntoIter<String, Vec<VersionedSourceFile>>;
285
286    fn into_iter(self) -> Self::IntoIter {
287        self.0.into_iter()
288    }
289}
290
291/// A [SourceFile] and the compiler version used to compile it
292#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
293pub struct VersionedSourceFile {
294    pub source_file: SourceFile,
295    pub version: Version,
296}