Skip to main content

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