foundry_compilers_core/utils/
wd.rs1use super::SOLC_EXTENSIONS;
2use crate::error::SolcError;
3use semver::Version;
4use std::{
5 collections::HashSet,
6 path::{Path, PathBuf},
7};
8use walkdir::WalkDir;
9
10pub fn source_files_iter<'a>(
15 root: &Path,
16 extensions: &'a [&'a str],
17) -> impl Iterator<Item = PathBuf> + 'a {
18 WalkDir::new(root)
19 .follow_links(true)
20 .into_iter()
21 .filter_map(Result::ok)
22 .filter(|e| e.file_type().is_file())
23 .filter(|e| {
24 e.path().extension().map(|ext| extensions.iter().any(|e| ext == *e)).unwrap_or_default()
25 })
26 .map(|e| e.path().into())
27}
28
29pub fn source_files(root: &Path, extensions: &[&str]) -> Vec<PathBuf> {
43 source_files_iter(root, extensions).collect()
44}
45
46pub fn sol_source_files(root: &Path) -> Vec<PathBuf> {
48 source_files(root, SOLC_EXTENSIONS)
49}
50
51pub fn solidity_dirs(root: &Path) -> Vec<PathBuf> {
81 let sources = sol_source_files(root);
82 sources
83 .iter()
84 .filter_map(|p| p.parent())
85 .collect::<HashSet<_>>()
86 .into_iter()
87 .map(|p| p.to_path_buf())
88 .collect()
89}
90
91pub fn installed_versions(root: &Path) -> Result<Vec<Version>, SolcError> {
98 let mut versions: Vec<_> = walkdir::WalkDir::new(root)
99 .max_depth(1)
100 .into_iter()
101 .filter_map(std::result::Result::ok)
102 .filter(|e| e.file_type().is_dir())
103 .filter_map(|e: walkdir::DirEntry| {
104 e.path().file_name().and_then(|v| Version::parse(v.to_string_lossy().as_ref()).ok())
105 })
106 .collect();
107 versions.sort();
108 Ok(versions)
109}
110
111pub fn find_case_sensitive_existing_file(non_existing: &Path) -> Option<PathBuf> {
113 let non_existing_file_name = non_existing.file_name()?;
114 let parent = non_existing.parent()?;
115 WalkDir::new(parent)
116 .max_depth(1)
117 .into_iter()
118 .filter_map(Result::ok)
119 .filter(|e| e.file_type().is_file())
120 .find_map(|e| {
121 let existing_file_name = e.path().file_name()?;
122 if existing_file_name.eq_ignore_ascii_case(non_existing_file_name)
123 && existing_file_name != non_existing_file_name
124 {
125 return Some(e.path().to_path_buf());
126 }
127 None
128 })
129}
130
131#[cfg(test)]
132mod tests {
133 use super::{super::tests::*, *};
134
135 #[test]
136 fn can_find_solidity_sources() {
137 let tmp_dir = tempdir("contracts").unwrap();
138
139 let file_a = tmp_dir.path().join("a.sol");
140 let file_b = tmp_dir.path().join("a.sol");
141 let nested = tmp_dir.path().join("nested");
142 let file_c = nested.join("c.sol");
143 let nested_deep = nested.join("deep");
144 let file_d = nested_deep.join("d.sol");
145 File::create(&file_a).unwrap();
146 File::create(&file_b).unwrap();
147 create_dir_all(nested_deep).unwrap();
148 File::create(&file_c).unwrap();
149 File::create(&file_d).unwrap();
150
151 let files: HashSet<_> = sol_source_files(tmp_dir.path()).into_iter().collect();
152 let expected: HashSet<_> = [file_a, file_b, file_c, file_d].into();
153 assert_eq!(files, expected);
154 }
155
156 #[test]
157 fn can_find_different_case() {
158 let tmp_dir = tempdir("out").unwrap();
159 let path = tmp_dir.path().join("forge-std");
160 create_dir_all(&path).unwrap();
161 let existing = path.join("Test.sol");
162 let non_existing = path.join("test.sol");
163 fs::write(&existing, b"").unwrap();
164
165 #[cfg(target_os = "linux")]
166 assert!(!non_existing.exists());
167
168 let found = find_case_sensitive_existing_file(&non_existing).unwrap();
169 assert_eq!(found, existing);
170 }
171}