_diffctx/config/
env_overrides.rs1pub fn parse_f64_or_default(raw: Option<String>, default: f64) -> f64 {
10 raw.and_then(|s| s.parse::<f64>().ok())
11 .filter(|v| v.is_finite() && *v >= 0.0)
12 .unwrap_or(default)
13}
14
15pub fn parse_fraction_or_default(raw: Option<String>, default: f64) -> f64 {
16 parse_f64_or_default(raw, default).clamp(0.0, 1.0)
17}
18
19pub fn parse_open_fraction_or_default(raw: Option<String>, default: f64) -> f64 {
23 const EPS: f64 = 1e-4;
24 parse_f64_or_default(raw, default).clamp(EPS, 1.0 - EPS)
25}
26
27pub fn parse_usize_or_default(raw: Option<String>, default: usize) -> usize {
28 raw.and_then(|s| s.parse::<usize>().ok()).unwrap_or(default)
29}
30
31pub fn parse_u32_or_default(raw: Option<String>, default: u32) -> u32 {
32 raw.and_then(|s| s.parse::<u32>().ok()).unwrap_or(default)
33}
34
35pub fn read_env_f64(name: &str, default: f64) -> f64 {
36 parse_f64_or_default(std::env::var(name).ok(), default)
37}
38
39pub fn read_env_fraction(name: &str, default: f64) -> f64 {
40 parse_fraction_or_default(std::env::var(name).ok(), default)
41}
42
43pub fn read_env_open_fraction(name: &str, default: f64) -> f64 {
44 parse_open_fraction_or_default(std::env::var(name).ok(), default)
45}
46
47pub fn read_env_usize(name: &str, default: usize) -> usize {
48 parse_usize_or_default(std::env::var(name).ok(), default)
49}
50
51pub fn read_env_u32(name: &str, default: u32) -> u32 {
52 parse_u32_or_default(std::env::var(name).ok(), default)
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn f64_accepts_finite_nonneg() {
61 assert_eq!(parse_f64_or_default(Some("0.42".into()), 1.0), 0.42);
62 assert_eq!(parse_f64_or_default(Some("0".into()), 1.0), 0.0);
63 }
64
65 #[test]
66 fn f64_rejects_negative_and_nonfinite() {
67 assert_eq!(parse_f64_or_default(Some("-0.5".into()), 1.0), 1.0);
68 assert_eq!(parse_f64_or_default(Some("nan".into()), 1.0), 1.0);
69 assert_eq!(parse_f64_or_default(Some("inf".into()), 1.0), 1.0);
70 }
71
72 #[test]
73 fn fraction_clamps_into_unit_interval() {
74 assert_eq!(parse_fraction_or_default(Some("0.5".into()), 0.7), 0.5);
75 assert_eq!(parse_fraction_or_default(Some("1.05".into()), 0.7), 1.0);
76 assert_eq!(parse_fraction_or_default(Some("42".into()), 0.7), 1.0);
77 assert_eq!(parse_fraction_or_default(Some("-0.5".into()), 0.7), 0.7);
78 assert_eq!(parse_fraction_or_default(Some("nan".into()), 0.7), 0.7);
79 assert_eq!(parse_fraction_or_default(None, 0.7), 0.7);
80 }
81
82 #[test]
83 fn open_fraction_clamps_to_open_interval() {
84 let v_one = parse_open_fraction_or_default(Some("1.0".into()), 0.6);
86 assert!(
87 v_one < 1.0,
88 "open fraction must clamp 1.0 below 1; got {v_one}"
89 );
90 assert!(
91 v_one > 0.99,
92 "clamp must stay near 1.0, not collapse to default"
93 );
94 let v_zero = parse_open_fraction_or_default(Some("0.0".into()), 0.6);
96 assert!(
97 v_zero > 0.0,
98 "open fraction must clamp 0.0 above 0; got {v_zero}"
99 );
100 assert_eq!(parse_open_fraction_or_default(Some("0.6".into()), 0.0), 0.6);
102 assert!(parse_open_fraction_or_default(Some("42".into()), 0.6) < 1.0);
104 }
105
106 #[test]
107 fn f64_falls_back_on_missing_or_unparseable() {
108 assert_eq!(parse_f64_or_default(None, 0.7), 0.7);
109 assert_eq!(parse_f64_or_default(Some("hello".into()), 0.7), 0.7);
110 assert_eq!(parse_f64_or_default(Some("".into()), 0.7), 0.7);
111 }
112
113 #[test]
114 fn usize_parses_or_falls_back() {
115 assert_eq!(parse_usize_or_default(Some("42".into()), 7), 42);
116 assert_eq!(parse_usize_or_default(Some("-1".into()), 7), 7);
117 assert_eq!(parse_usize_or_default(None, 7), 7);
118 }
119
120 #[test]
121 fn u32_parses_or_falls_back() {
122 assert_eq!(parse_u32_or_default(Some("24".into()), 8), 24);
123 assert_eq!(parse_u32_or_default(Some("nope".into()), 8), 8);
124 }
125}