1use std::sync::OnceLock;
2
3fn env_flag_enabled(compile_time: Option<&str>, runtime: Option<String>) -> bool {
9 if let Some(val) = runtime {
10 val.eq_ignore_ascii_case("1") || val.eq_ignore_ascii_case("true")
11 } else if let Some(val) = compile_time {
12 val.eq_ignore_ascii_case("1") || val.eq_ignore_ascii_case("true")
13 } else {
14 false
15 }
16}
17
18fn env_str(compile_time: Option<&'static str>, runtime: Option<String>) -> Option<String> {
28 fn non_empty(value: Option<String>) -> Option<String> {
29 value.filter(|v| !v.is_empty())
30 }
31 non_empty(runtime).or_else(|| non_empty(compile_time.map(str::to_string)))
32}
33
34fn parse_max_texture_size(raw: Option<String>) -> u32 {
38 raw.and_then(|s| s.parse().ok()).unwrap_or(0)
39}
40
41fn parse_atlas_size(raw: Option<String>) -> Option<(u32, u32)> {
45 raw.and_then(|s| {
46 let parts: Vec<&str> = s.split('x').collect();
47 if parts.len() == 2 {
48 let w = parts[0].parse::<u32>().ok()?;
49 let h = parts[1].parse::<u32>().ok()?;
50 Some((w, h))
51 } else {
52 None
53 }
54 })
55}
56
57pub fn depth_disabled() -> bool {
60 static DISABLED: OnceLock<bool> = OnceLock::new();
61 *DISABLED.get_or_init(|| {
62 env_flag_enabled(
63 option_env!("FRUST_ENGINE_NO_DEPTH"),
64 std::env::var("FRUST_ENGINE_NO_DEPTH").ok(),
65 )
66 })
67}
68
69pub fn atlas_disabled() -> bool {
75 static DISABLED: OnceLock<bool> = OnceLock::new();
76 *DISABLED.get_or_init(|| {
77 env_flag_enabled(
78 option_env!("FRUST_ENGINE_NO_ATLAS"),
79 std::env::var("FRUST_ENGINE_NO_ATLAS").ok(),
80 )
81 })
82}
83
84pub fn shader_effects_disabled() -> bool {
94 static DISABLED: OnceLock<bool> = OnceLock::new();
95 *DISABLED.get_or_init(|| {
96 env_flag_enabled(
97 option_env!("FRUST_ENGINE_NO_SHADER_EFFECTS"),
98 std::env::var("FRUST_ENGINE_NO_SHADER_EFFECTS").ok(),
99 )
100 })
101}
102
103pub fn layers_disabled() -> bool {
109 static DISABLED: OnceLock<bool> = OnceLock::new();
110 *DISABLED.get_or_init(|| {
111 env_flag_enabled(
112 option_env!("FRUST_ENGINE_NO_LAYERS"),
113 std::env::var("FRUST_ENGINE_NO_LAYERS").ok(),
114 )
115 })
116}
117
118pub fn pool_disabled() -> bool {
124 static DISABLED: OnceLock<bool> = OnceLock::new();
125 *DISABLED.get_or_init(|| {
126 env_flag_enabled(
127 option_env!("FRUST_ENGINE_NO_POOL"),
128 std::env::var("FRUST_ENGINE_NO_POOL").ok(),
129 )
130 })
131}
132
133pub fn max_texture_size() -> u32 {
139 static MAX_SIZE: OnceLock<u32> = OnceLock::new();
140 *MAX_SIZE.get_or_init(|| {
141 parse_max_texture_size(env_str(
142 option_env!("FRUST_ENGINE_MAX_TEX"),
143 std::env::var("FRUST_ENGINE_MAX_TEX").ok(),
144 ))
145 })
146}
147
148pub fn atlas_size() -> Option<(u32, u32)> {
155 static ATLAS_SIZE: OnceLock<Option<(u32, u32)>> = OnceLock::new();
156 *ATLAS_SIZE.get_or_init(|| {
157 parse_atlas_size(env_str(
158 option_env!("FRUST_ENGINE_ATLAS_SIZE"),
159 std::env::var("FRUST_ENGINE_ATLAS_SIZE").ok(),
160 ))
161 })
162}
163
164pub fn downlevel_mode() -> bool {
167 static DOWNLEVEL: OnceLock<bool> = OnceLock::new();
168 *DOWNLEVEL.get_or_init(|| {
169 env_flag_enabled(
170 option_env!("FRUST_ENGINE_DOWNLEVEL"),
171 std::env::var("FRUST_ENGINE_DOWNLEVEL").ok(),
172 )
173 })
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_env_flag_enabled() {
182 assert!(env_flag_enabled(Some("1"), None));
183 assert!(env_flag_enabled(Some("true"), None));
184 assert!(env_flag_enabled(Some("TRUE"), None));
185 assert!(!env_flag_enabled(Some("0"), None));
186 assert!(!env_flag_enabled(Some("false"), None));
187 assert!(!env_flag_enabled(None, None));
188 }
189
190 #[test]
191 fn test_runtime_overrides_compile_time() {
192 assert!(env_flag_enabled(Some("0"), Some("1".to_string())));
193 assert!(!env_flag_enabled(Some("1"), Some("0".to_string())));
194 }
195
196 #[test]
197 fn test_env_str_unset_is_none() {
198 assert_eq!(env_str(None, None), None);
199 }
200
201 #[test]
202 fn test_env_str_runtime_wins_over_compile_time() {
203 assert_eq!(
204 env_str(Some("1024"), Some("2048".to_string())),
205 Some("2048".to_string())
206 );
207 }
208
209 #[test]
210 fn test_env_str_empty_runtime_falls_through_to_compile_time() {
211 assert_eq!(
212 env_str(Some("1024"), Some(String::new())),
213 Some("1024".to_string())
214 );
215 }
216
217 #[test]
218 fn test_env_str_compile_time_only() {
219 assert_eq!(env_str(Some("1024"), None), Some("1024".to_string()));
220 }
221
222 #[test]
223 fn test_env_str_runtime_only() {
224 assert_eq!(
225 env_str(None, Some("2048".to_string())),
226 Some("2048".to_string())
227 );
228 }
229
230 #[test]
235 fn shader_effects_are_enabled_unless_the_switch_is_set() {
236 assert!(!shader_effects_disabled());
237 }
238
239 #[test]
244 fn shader_effects_switch_parses_like_every_other_engine_flag() {
245 assert!(env_flag_enabled(None, Some("1".to_string())));
246 assert!(env_flag_enabled(None, Some("TRUE".to_string())));
247 assert!(!env_flag_enabled(None, Some("0".to_string())));
248 assert!(!env_flag_enabled(None, Some(String::new())));
249 assert!(env_flag_enabled(Some("0"), Some("1".to_string())));
251 assert!(!env_flag_enabled(Some("1"), Some("0".to_string())));
252 }
253
254 #[test]
255 fn test_max_texture_size_parsing() {
256 assert_eq!(parse_max_texture_size(Some("1024".to_string())), 1024);
257 assert_eq!(parse_max_texture_size(Some("0".to_string())), 0);
258 assert_eq!(parse_max_texture_size(Some("invalid".to_string())), 0);
259 assert_eq!(parse_max_texture_size(None), 0);
260 }
261
262 #[test]
263 fn test_atlas_size_parsing() {
264 assert_eq!(
265 parse_atlas_size(Some("2048x2048".to_string())),
266 Some((2048, 2048))
267 );
268 assert_eq!(
269 parse_atlas_size(Some("1024x512".to_string())),
270 Some((1024, 512))
271 );
272 assert_eq!(parse_atlas_size(Some("2048".to_string())), None);
273 assert_eq!(parse_atlas_size(Some("2048x2048x2048".to_string())), None);
274 assert_eq!(parse_atlas_size(None), None);
275 }
276}