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}