Skip to content

Commit fd1c051

Browse files
authored
refactor: unify sources and filtered sources (foundry-rs#162)
Allows reusing resources and just doing `retain` instead of `.into_iter().collect()` on 3 nested maps
1 parent bc96471 commit fd1c051

File tree

8 files changed

+207
-261
lines changed

8 files changed

+207
-261
lines changed

crates/artifacts/solc/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ impl SolcInput {
106106
/// Builds one or two inputs from given sources set. Returns two inputs in cases when there are
107107
/// both Solidity and Yul sources.
108108
pub fn resolve_and_build(sources: Sources, settings: Settings) -> Vec<Self> {
109-
let mut solidity_sources = BTreeMap::new();
110-
let mut yul_sources = BTreeMap::new();
109+
let mut solidity_sources = Sources::new();
110+
let mut yul_sources = Sources::new();
111111

112112
for (file, source) in sources {
113113
if file.extension().map_or(false, |e| e == "yul") {

crates/artifacts/solc/src/sources.rs

Lines changed: 119 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,97 @@ use std::{
99
sync::Arc,
1010
};
1111

12-
/// An ordered list of files and their source
13-
pub type Sources = BTreeMap<PathBuf, Source>;
12+
type SourcesInner = BTreeMap<PathBuf, Source>;
13+
14+
/// An ordered list of files and their source.
15+
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
16+
pub struct Sources(pub SourcesInner);
17+
18+
impl Sources {
19+
/// Returns a new instance of [Sources].
20+
pub fn new() -> Self {
21+
Self::default()
22+
}
23+
24+
/// Returns `true` if no sources should have optimized output selection.
25+
pub fn all_dirty(&self) -> bool {
26+
self.0.values().all(|s| s.is_dirty())
27+
}
28+
29+
/// Returns all entries that should not be optimized.
30+
pub fn dirty(&self) -> impl Iterator<Item = (&PathBuf, &Source)> + '_ {
31+
self.0.iter().filter(|(_, s)| s.is_dirty())
32+
}
33+
34+
/// Returns all entries that should be optimized.
35+
pub fn clean(&self) -> impl Iterator<Item = (&PathBuf, &Source)> + '_ {
36+
self.0.iter().filter(|(_, s)| !s.is_dirty())
37+
}
38+
39+
/// Returns all files that should not be optimized.
40+
pub fn dirty_files(&self) -> impl Iterator<Item = &PathBuf> + '_ {
41+
self.dirty().map(|(k, _)| k)
42+
}
43+
}
44+
45+
impl std::ops::Deref for Sources {
46+
type Target = SourcesInner;
47+
48+
fn deref(&self) -> &Self::Target {
49+
&self.0
50+
}
51+
}
52+
53+
impl std::ops::DerefMut for Sources {
54+
fn deref_mut(&mut self) -> &mut Self::Target {
55+
&mut self.0
56+
}
57+
}
58+
59+
impl<I> From<I> for Sources
60+
where
61+
SourcesInner: From<I>,
62+
{
63+
fn from(value: I) -> Self {
64+
Self(From::from(value))
65+
}
66+
}
67+
68+
impl<I> FromIterator<I> for Sources
69+
where
70+
SourcesInner: FromIterator<I>,
71+
{
72+
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
73+
Self(FromIterator::from_iter(iter))
74+
}
75+
}
76+
77+
impl IntoIterator for Sources {
78+
type Item = <SourcesInner as IntoIterator>::Item;
79+
type IntoIter = <SourcesInner as IntoIterator>::IntoIter;
80+
81+
fn into_iter(self) -> Self::IntoIter {
82+
self.0.into_iter()
83+
}
84+
}
85+
86+
impl<'a> IntoIterator for &'a Sources {
87+
type Item = <&'a SourcesInner as IntoIterator>::Item;
88+
type IntoIter = <&'a SourcesInner as IntoIterator>::IntoIter;
89+
90+
fn into_iter(self) -> Self::IntoIter {
91+
self.0.iter()
92+
}
93+
}
94+
95+
impl<'a> IntoIterator for &'a mut Sources {
96+
type Item = <&'a mut SourcesInner as IntoIterator>::Item;
97+
type IntoIter = <&'a mut SourcesInner as IntoIterator>::IntoIter;
98+
99+
fn into_iter(self) -> Self::IntoIter {
100+
self.0.iter_mut()
101+
}
102+
}
14103

15104
/// Content of a solidity file
16105
///
@@ -23,12 +112,14 @@ pub struct Source {
23112
/// conflicting versions then the same [Source] may be required by conflicting versions and
24113
/// needs to be duplicated.
25114
pub content: Arc<String>,
115+
#[serde(skip, default)]
116+
pub kind: SourceCompilationKind,
26117
}
27118

28119
impl Source {
29120
/// Creates a new instance of [Source] with the given content.
30121
pub fn new(content: impl Into<String>) -> Self {
31-
Self { content: Arc::new(content.into()) }
122+
Self { content: Arc::new(content.into()), kind: SourceCompilationKind::Complete }
32123
}
33124

34125
/// Reads the file's content
@@ -45,6 +136,11 @@ impl Source {
45136
Ok(Self::new(content))
46137
}
47138

139+
/// Returns `true` if the source should be compiled with full output selection.
140+
pub fn is_dirty(&self) -> bool {
141+
self.kind.is_dirty()
142+
}
143+
48144
/// Recursively finds all source files under the given dir path and reads them all
49145
pub fn read_all_from(dir: &Path, extensions: &[&str]) -> Result<Sources, SolcIoError> {
50146
Self::read_all_files(utils::source_files(dir, extensions))
@@ -91,7 +187,8 @@ impl Source {
91187
.par_bridge()
92188
.map(Into::into)
93189
.map(|file| Self::read(&file).map(|source| (file, source)))
94-
.collect()
190+
.collect::<Result<BTreeMap<_, _>, _>>()
191+
.map(Sources)
95192
}
96193

97194
/// Generate a non-cryptographically secure checksum of the file's content
@@ -156,3 +253,21 @@ impl AsRef<[u8]> for Source {
156253
self.content.as_bytes()
157254
}
158255
}
256+
257+
/// Represents the state of a filtered [`Source`].
258+
#[derive(Clone, Debug, Default, PartialEq, Eq)]
259+
pub enum SourceCompilationKind {
260+
/// We need a complete compilation output for the source.
261+
#[default]
262+
Complete,
263+
/// A source for which we don't need a complete output and want to optimize its compilation by
264+
/// reducing output selection.
265+
Optimized,
266+
}
267+
268+
impl SourceCompilationKind {
269+
/// Whether this file should be compiled with full output selection
270+
pub fn is_dirty(&self) -> bool {
271+
matches!(self, Self::Complete)
272+
}
273+
}

crates/compilers/src/buildinfo.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,16 @@ impl<L: Language> RawBuildInfo<L> {
128128

129129
#[cfg(test)]
130130
mod tests {
131-
use foundry_compilers_artifacts::{sources::Source, Error, SolcLanguage};
132-
133-
use crate::compilers::solc::SolcVersionedInput;
134-
135131
use super::*;
136-
use std::{collections::BTreeMap, path::PathBuf};
132+
use crate::compilers::solc::SolcVersionedInput;
133+
use foundry_compilers_artifacts::{sources::Source, Error, SolcLanguage, Sources};
134+
use std::path::PathBuf;
137135

138136
#[test]
139137
fn build_info_serde() {
140138
let v: Version = "0.8.4+commit.c7e474f2".parse().unwrap();
141139
let input = SolcVersionedInput::build(
142-
BTreeMap::from([(PathBuf::from("input.sol"), Source::new(""))]),
140+
Sources::from([(PathBuf::from("input.sol"), Source::new(""))]),
143141
Default::default(),
144142
SolcLanguage::Solidity,
145143
v,

crates/compilers/src/cache.rs

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use crate::{
55
compilers::{Compiler, CompilerSettings, Language},
66
output::Builds,
77
resolver::GraphEdges,
8-
ArtifactFile, ArtifactOutput, Artifacts, ArtifactsMap, FilteredSources, Graph, OutputContext,
9-
Project, ProjectPaths, ProjectPathsConfig, SourceCompilationKind,
8+
ArtifactFile, ArtifactOutput, Artifacts, ArtifactsMap, Graph, OutputContext, Project,
9+
ProjectPaths, ProjectPathsConfig, SourceCompilationKind,
1010
};
1111
use foundry_compilers_artifacts::{
1212
sources::{Source, Sources},
@@ -648,10 +648,10 @@ impl<'a, T: ArtifactOutput, C: Compiler> ArtifactsCacheInner<'a, T, C> {
648648
/// 2. [SourceCompilationKind::Optimized] - the file is not dirty, but is imported by a dirty
649649
/// file and thus will be processed by solc. For such files we don't need full data, so we
650650
/// are marking them as clean to optimize output selection later.
651-
fn filter(&mut self, sources: Sources, version: &Version) -> FilteredSources {
651+
fn filter(&mut self, sources: &mut Sources, version: &Version) {
652652
// sources that should be passed to compiler.
653-
let mut compile_complete = BTreeSet::new();
654-
let mut compile_optimized = BTreeSet::new();
653+
let mut compile_complete = HashSet::new();
654+
let mut compile_optimized = HashSet::new();
655655

656656
for (file, source) in sources.iter() {
657657
self.sources_in_scope.insert(file.clone(), version.clone());
@@ -677,20 +677,16 @@ impl<'a, T: ArtifactOutput, C: Compiler> ArtifactsCacheInner<'a, T, C> {
677677
}
678678
}
679679

680-
let filtered = sources
681-
.into_iter()
682-
.filter_map(|(file, source)| {
683-
if compile_complete.contains(&file) {
684-
Some((file, SourceCompilationKind::Complete(source)))
685-
} else if compile_optimized.contains(&file) {
686-
Some((file, SourceCompilationKind::Optimized(source)))
687-
} else {
688-
None
689-
}
690-
})
691-
.collect();
692-
693-
FilteredSources(filtered)
680+
sources.retain(|file, source| {
681+
source.kind = if compile_complete.contains(file) {
682+
SourceCompilationKind::Complete
683+
} else if compile_optimized.contains(file) {
684+
SourceCompilationKind::Optimized
685+
} else {
686+
return false;
687+
};
688+
true
689+
});
694690
}
695691

696692
/// Returns whether we are missing artifacts for the given file and version.
@@ -746,7 +742,7 @@ impl<'a, T: ArtifactOutput, C: Compiler> ArtifactsCacheInner<'a, T, C> {
746742
// Iterate over existing cache entries.
747743
let files = self.cache.files.keys().cloned().collect::<HashSet<_>>();
748744

749-
let mut sources = BTreeMap::new();
745+
let mut sources = Sources::new();
750746

751747
// Read all sources, marking entries as dirty on I/O errors.
752748
for file in &files {
@@ -939,14 +935,14 @@ impl<'a, T: ArtifactOutput, C: Compiler> ArtifactsCache<'a, T, C> {
939935
// only useful for debugging for debugging purposes
940936
pub fn as_cached(&self) -> Option<&ArtifactsCacheInner<'a, T, C>> {
941937
match self {
942-
ArtifactsCache::Ephemeral(_, _) => None,
938+
ArtifactsCache::Ephemeral(..) => None,
943939
ArtifactsCache::Cached(cached) => Some(cached),
944940
}
945941
}
946942

947943
pub fn output_ctx(&self) -> OutputContext<'_> {
948944
match self {
949-
ArtifactsCache::Ephemeral(_, _) => Default::default(),
945+
ArtifactsCache::Ephemeral(..) => Default::default(),
950946
ArtifactsCache::Cached(inner) => OutputContext::new(&inner.cache),
951947
}
952948
}
@@ -961,15 +957,15 @@ impl<'a, T: ArtifactOutput, C: Compiler> ArtifactsCache<'a, T, C> {
961957
/// Adds the file's hashes to the set if not set yet
962958
pub fn remove_dirty_sources(&mut self) {
963959
match self {
964-
ArtifactsCache::Ephemeral(_, _) => {}
960+
ArtifactsCache::Ephemeral(..) => {}
965961
ArtifactsCache::Cached(cache) => cache.find_and_remove_dirty(),
966962
}
967963
}
968964

969965
/// Filters out those sources that don't need to be compiled
970-
pub fn filter(&mut self, sources: Sources, version: &Version) -> FilteredSources {
966+
pub fn filter(&mut self, sources: &mut Sources, version: &Version) {
971967
match self {
972-
ArtifactsCache::Ephemeral(_, _) => sources.into(),
968+
ArtifactsCache::Ephemeral(..) => {}
973969
ArtifactsCache::Cached(cache) => cache.filter(sources, version),
974970
}
975971
}

0 commit comments

Comments
 (0)