1use std::env;
2use std::fs::File;
3use std::path::PathBuf;
4
5use std::io::{BufRead, BufReader};
6use std::sync::OnceLock;
7
8#[derive(Debug, Clone)]
9struct IgnorePattern {
10 os: Option<String>,
11 arch: Option<String>,
12 target_env: Option<String>,
13 engine: Option<String>,
14 compiler: Option<String>,
15 pattern_to_ignore: String,
16}
17
18impl IgnorePattern {
19 fn should_ignore(
20 &self,
21 os: &str,
22 arch: &str,
23 target_env: &str,
24 engine: &str,
25 compiler: &str,
26 canonical_path: &str,
27 ) -> bool {
28 self.os.as_ref().is_none_or(|val| val == os)
29 && self.arch.as_ref().is_none_or(|val| val == arch)
30 && self.target_env.as_ref().is_none_or(|val| val == target_env)
31 && self.engine.as_ref().is_none_or(|val| val == engine)
32 && self.compiler.as_ref().is_none_or(|val| val == compiler)
33 && (self.pattern_to_ignore == "*" || canonical_path.contains(&*self.pattern_to_ignore))
34 }
35}
36
37#[derive(Debug, Clone)]
38pub struct Ignores {
39 patterns: Vec<IgnorePattern>,
41}
42
43impl Ignores {
44 pub fn should_ignore(
46 &self,
47 os: &str,
48 arch: &str,
49 target_env: &str,
50 engine: &str,
51 compiler: &str,
52 canonical_path: &str,
53 ) -> bool {
54 self.patterns.iter().any(|p| {
55 p.should_ignore(os, arch, target_env, engine, compiler, canonical_path)
57 })
58 }
59
60 pub fn should_ignore_host(&self, engine: &str, compiler: &str, canonical_path: &str) -> bool {
61 static CFG_TARGET_OS: OnceLock<String> = OnceLock::new();
62 let target_os = CFG_TARGET_OS.get_or_init(|| {
63 env::var("CFG_TARGET_OS")
64 .expect("CFG_TARGET_OS variable expected from build.rs")
65 .to_string()
66 });
67 static CFG_TARGET_ARCH: OnceLock<String> = OnceLock::new();
68 let target_arch = CFG_TARGET_ARCH.get_or_init(|| {
69 env::var("CFG_TARGET_ARCH")
70 .expect("CFG_TARGET_ARCH variable expected from build.rs")
71 .to_string()
72 });
73 static CFG_TARGET_ENV: OnceLock<String> = OnceLock::new();
74 let target_env = CFG_TARGET_ENV.get_or_init(|| {
75 env::var("CFG_TARGET_ENV")
76 .expect("CFG_TARGET_ENV variable expected from build.rs")
77 .to_string()
78 });
79
80 self.should_ignore(
81 target_os,
82 target_arch,
83 target_env,
84 engine,
85 compiler,
86 canonical_path,
87 )
88 }
89
90 pub fn build_from_path(path: PathBuf) -> Ignores {
92 let file = File::open(path).unwrap();
93 let reader = BufReader::new(file);
94 let mut patterns = Vec::new();
95
96 for (i, line) in reader.lines().enumerate() {
97 let line = line.unwrap();
98 let line = line
100 .split_once("#")
101 .map(|(line, _comment)| line)
102 .unwrap_or_else(|| &line)
103 .trim()
104 .to_string();
105
106 if let Some((platform_specifier, pattern_to_ignore)) = line.split_once(" ") {
109 let mut os: Option<String> = None;
110 let mut arch: Option<String> = None;
111 let mut target_env: Option<String> = None;
112 let mut engine: Option<String> = None;
113 let mut compiler: Option<String> = None;
114 for alias in platform_specifier.trim().split('+') {
115 match alias {
116 "windows" | "macos" | "linux" => {
118 os = Some(alias.to_string());
119 }
120 "musl" => {
122 target_env = Some(alias.to_string());
123 }
124 "aarch64" | "x86" | "x64" | "riscv64" | "loongarch64" => {
126 arch = Some(alias.to_string());
127 }
128 "engine" => {
130 engine = Some(alias.to_string());
131 }
132 "cranelift" | "llvm" | "singlepass" | "v8" => {
134 compiler = Some(alias.to_string());
135 }
136 other => {
137 panic!(
138 "Alias {:?} not currently supported (defined in ignores.txt in line {})",
139 other,
140 i + 1
141 );
142 }
143 }
144 }
145 let pattern_to_ignore = pattern_to_ignore.trim().to_string();
146 patterns.push(IgnorePattern {
147 os,
148 arch,
149 target_env,
150 engine,
151 compiler,
152 pattern_to_ignore,
153 });
154 } else {
155 if line.is_empty() {
156 continue;
157 }
158 patterns.push(IgnorePattern {
159 os: None,
160 arch: None,
161 target_env: None,
162 engine: None,
163 compiler: None,
164 pattern_to_ignore: line,
165 });
166 };
167 }
168 Ignores { patterns }
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn features_match() -> Result<(), ()> {
178 assert!(
179 IgnorePattern {
180 os: None,
181 arch: None,
182 target_env: None,
183 engine: None,
184 compiler: None,
185 pattern_to_ignore: "*".to_string()
186 }
187 .should_ignore(
188 "unknown",
189 "unknown",
190 "",
191 "engine",
192 "compiler",
193 "some::random::text"
194 )
195 );
196 assert!(
197 IgnorePattern {
198 os: None,
199 arch: None,
200 target_env: None,
201 engine: None,
202 compiler: None,
203 pattern_to_ignore: "some::random".to_string()
204 }
205 .should_ignore(
206 "unknown",
207 "unknown",
208 "",
209 "engine",
210 "compiler",
211 "some::random::text"
212 )
213 );
214 assert!(
215 !IgnorePattern {
216 os: Some("macos".to_string()),
217 arch: None,
218 target_env: None,
219 engine: None,
220 compiler: None,
221 pattern_to_ignore: "other".to_string()
222 }
223 .should_ignore(
224 "unknown",
225 "unknown",
226 "",
227 "engine",
228 "compiler",
229 "some::random::text"
230 )
231 );
232 assert!(
233 !IgnorePattern {
234 os: Some("macos".to_string()),
235 arch: None,
236 target_env: None,
237 engine: Some("engine".to_string()),
238 compiler: None,
239 pattern_to_ignore: "other".to_string()
240 }
241 .should_ignore(
242 "macos",
243 "unknown",
244 "",
245 "engine",
246 "compiler",
247 "some::random::text"
248 )
249 );
250 Ok(())
251 }
252}