foundry_compilers/compile/output/
sources.rs

1use crate::SourceFile;
2use foundry_compilers_core::utils::strip_prefix_owned;
3use semver::Version;
4use serde::{Deserialize, Serialize};
5use std::{
6    collections::BTreeMap,
7    path::{Path, PathBuf},
8};
9
10/// (source_file path  -> `SourceFile` + solc version)
11#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(transparent)]
13pub struct VersionedSourceFiles(pub BTreeMap<PathBuf, Vec<VersionedSourceFile>>);
14
15impl VersionedSourceFiles {
16    /// Converts all `\\` separators in _all_ paths to `/`
17    pub fn slash_paths(&mut self) {
18        #[cfg(windows)]
19        {
20            use path_slash::PathExt;
21            self.0 = std::mem::take(&mut self.0)
22                .into_iter()
23                .map(|(path, files)| (PathBuf::from(path.to_slash_lossy().as_ref()), files))
24                .collect()
25        }
26    }
27
28    pub fn is_empty(&self) -> bool {
29        self.0.is_empty()
30    }
31
32    pub fn len(&self) -> usize {
33        self.0.len()
34    }
35
36    /// Returns an iterator over all files
37    pub fn files(&self) -> impl Iterator<Item = &PathBuf> {
38        self.0.keys()
39    }
40
41    /// Returns an iterator over the source files' IDs and path.
42    pub fn into_ids(self) -> impl Iterator<Item = (u32, PathBuf)> {
43        self.into_sources().map(|(path, source)| (source.id, path))
44    }
45
46    /// Returns an iterator over the source files' paths and IDs.
47    pub fn into_paths(self) -> impl Iterator<Item = (PathBuf, u32)> {
48        self.into_ids().map(|(id, path)| (path, id))
49    }
50
51    /// Returns an iterator over the source files' IDs and path.
52    pub fn into_ids_with_version(self) -> impl Iterator<Item = (u32, PathBuf, Version)> {
53        self.into_sources_with_version().map(|(path, source, version)| (source.id, path, version))
54    }
55
56    /// Finds the _first_ source file with the given path.
57    ///
58    /// # Examples
59    /// ```no_run
60    /// use foundry_compilers::{artifacts::*, Project};
61    ///
62    /// let project = Project::builder().build(Default::default())?;
63    /// let output = project.compile()?.into_output();
64    /// let source_file = output.sources.find_file("src/Greeter.sol".as_ref()).unwrap();
65    /// # Ok::<_, Box<dyn std::error::Error>>(())
66    /// ```
67    pub fn find_file(&self, path: &Path) -> Option<&SourceFile> {
68        self.sources().find(|&(p, _)| p == path).map(|(_, sf)| sf)
69    }
70
71    /// Same as [Self::find_file] but also checks for version
72    pub fn find_file_and_version(&self, path: &Path, version: &Version) -> Option<&SourceFile> {
73        self.0.get(path).and_then(|contracts| {
74            contracts.iter().find_map(|source| {
75                if source.version == *version {
76                    Some(&source.source_file)
77                } else {
78                    None
79                }
80            })
81        })
82    }
83
84    /// Finds the _first_ source file with the given id
85    ///
86    /// # Examples
87    /// ```no_run
88    /// use foundry_compilers::{artifacts::*, Project};
89    ///
90    /// let project = Project::builder().build(Default::default())?;
91    /// let output = project.compile()?.into_output();
92    /// let source_file = output.sources.find_id(0).unwrap();
93    /// # Ok::<_, Box<dyn std::error::Error>>(())
94    /// ```
95    pub fn find_id(&self, id: u32) -> Option<&SourceFile> {
96        self.sources().filter(|(_, source)| source.id == id).map(|(_, source)| source).next()
97    }
98
99    /// Same as [Self::find_id] but also checks for version
100    pub fn find_id_and_version(&self, id: u32, version: &Version) -> Option<&SourceFile> {
101        self.sources_with_version()
102            .filter(|(_, source, v)| source.id == id && *v == version)
103            .map(|(_, source, _)| source)
104            .next()
105    }
106
107    /// Removes the _first_ source_file with the given path from the set
108    ///
109    /// # Examples
110    /// ```no_run
111    /// use foundry_compilers::{artifacts::*, Project};
112    ///
113    /// let project = Project::builder().build(Default::default())?;
114    /// let (mut sources, _) = project.compile()?.into_output().split();
115    /// let source_file = sources.remove_by_path("src/Greeter.sol".as_ref()).unwrap();
116    /// # Ok::<_, Box<dyn std::error::Error>>(())
117    /// ```
118    pub fn remove_by_path(&mut self, path: &Path) -> Option<SourceFile> {
119        self.0.get_mut(path).and_then(|all_sources| {
120            if !all_sources.is_empty() {
121                Some(all_sources.remove(0).source_file)
122            } else {
123                None
124            }
125        })
126    }
127
128    /// Removes the _first_ source_file with the given id from the set
129    ///
130    /// # Examples
131    /// ```no_run
132    /// use foundry_compilers::{artifacts::*, Project};
133    ///
134    /// let project = Project::builder().build(Default::default())?;
135    /// let (mut sources, _) = project.compile()?.into_output().split();
136    /// let source_file = sources.remove_by_id(0).unwrap();
137    /// # Ok::<_, Box<dyn std::error::Error>>(())
138    /// ```
139    pub fn remove_by_id(&mut self, id: u32) -> Option<SourceFile> {
140        self.0
141            .values_mut()
142            .filter_map(|sources| {
143                sources
144                    .iter()
145                    .position(|source| source.source_file.id == id)
146                    .map(|pos| sources.remove(pos).source_file)
147            })
148            .next()
149    }
150
151    /// Returns an iterator over all contracts and their names.
152    pub fn sources(&self) -> impl Iterator<Item = (&PathBuf, &SourceFile)> {
153        self.0.iter().flat_map(|(path, sources)| {
154            sources.iter().map(move |source| (path, &source.source_file))
155        })
156    }
157
158    /// Returns an iterator over (`file`,  `SourceFile`, `Version`)
159    pub fn sources_with_version(&self) -> impl Iterator<Item = (&PathBuf, &SourceFile, &Version)> {
160        self.0.iter().flat_map(|(file, sources)| {
161            sources.iter().map(move |c| (file, &c.source_file, &c.version))
162        })
163    }
164
165    /// Returns an iterator over all contracts and their source names.
166    pub fn into_sources(self) -> impl Iterator<Item = (PathBuf, SourceFile)> {
167        self.0.into_iter().flat_map(|(path, sources)| {
168            sources.into_iter().map(move |source| (path.clone(), source.source_file))
169        })
170    }
171
172    /// Returns an iterator over all contracts and their source names.
173    pub fn into_sources_with_version(self) -> impl Iterator<Item = (PathBuf, SourceFile, Version)> {
174        self.0.into_iter().flat_map(|(path, sources)| {
175            sources
176                .into_iter()
177                .map(move |source| (path.clone(), source.source_file, source.version))
178        })
179    }
180
181    /// Sets the sources' file paths to `root` adjoined to `self.file`.
182    pub fn join_all(&mut self, root: &Path) -> &mut Self {
183        self.0 = std::mem::take(&mut self.0)
184            .into_iter()
185            .map(|(file_path, sources)| (root.join(file_path), sources))
186            .collect();
187        self
188    }
189
190    /// Removes `base` from all source file paths
191    pub fn strip_prefix_all(&mut self, base: &Path) -> &mut Self {
192        self.0 = std::mem::take(&mut self.0)
193            .into_iter()
194            .map(|(file, sources)| (strip_prefix_owned(file, base), sources))
195            .collect();
196        self
197    }
198}
199
200impl AsRef<BTreeMap<PathBuf, Vec<VersionedSourceFile>>> for VersionedSourceFiles {
201    fn as_ref(&self) -> &BTreeMap<PathBuf, Vec<VersionedSourceFile>> {
202        &self.0
203    }
204}
205
206impl AsMut<BTreeMap<PathBuf, Vec<VersionedSourceFile>>> for VersionedSourceFiles {
207    fn as_mut(&mut self) -> &mut BTreeMap<PathBuf, Vec<VersionedSourceFile>> {
208        &mut self.0
209    }
210}
211
212impl IntoIterator for VersionedSourceFiles {
213    type Item = (PathBuf, Vec<VersionedSourceFile>);
214    type IntoIter = std::collections::btree_map::IntoIter<PathBuf, Vec<VersionedSourceFile>>;
215
216    fn into_iter(self) -> Self::IntoIter {
217        self.0.into_iter()
218    }
219}
220
221/// A [SourceFile] and the compiler version used to compile it
222#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
223pub struct VersionedSourceFile {
224    pub source_file: SourceFile,
225    pub version: Version,
226    pub build_id: String,
227    pub profile: String,
228}