• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::gen::{CfgEvaluator, CfgResult};
2 use once_cell::sync::OnceCell;
3 use std::borrow::Borrow;
4 use std::cmp::Ordering;
5 use std::collections::{BTreeMap as Map, BTreeSet as Set};
6 use std::env;
7 
8 static ENV: OnceCell<CargoEnv> = OnceCell::new();
9 
10 struct CargoEnv {
11     features: Set<Name>,
12     cfgs: Map<Name, String>,
13 }
14 
15 pub(super) struct CargoEnvCfgEvaluator;
16 
17 impl CfgEvaluator for CargoEnvCfgEvaluator {
eval(&self, name: &str, query_value: Option<&str>) -> CfgResult18     fn eval(&self, name: &str, query_value: Option<&str>) -> CfgResult {
19         let env = ENV.get_or_init(CargoEnv::load);
20         if name == "feature" {
21             return if let Some(query_value) = query_value {
22                 CfgResult::from(env.features.contains(Lookup::new(query_value)))
23             } else {
24                 let msg = "expected `feature = \"...\"`".to_owned();
25                 CfgResult::Undetermined { msg }
26             };
27         }
28         if name == "test" && query_value.is_none() {
29             let msg = "cfg(test) is not supported because Cargo runs your build script only once across the lib and test build of the same crate".to_owned();
30             return CfgResult::Undetermined { msg };
31         }
32         if let Some(cargo_value) = env.cfgs.get(Lookup::new(name)) {
33             return if let Some(query_value) = query_value {
34                 CfgResult::from(cargo_value.split(',').any(|value| value == query_value))
35             } else {
36                 CfgResult::True
37             };
38         }
39         if name == "debug_assertions" && query_value.is_none() {
40             return CfgResult::from(cfg!(debug_assertions));
41         }
42         CfgResult::False
43     }
44 }
45 
46 impl CargoEnv {
load() -> Self47     fn load() -> Self {
48         const CARGO_FEATURE_PREFIX: &str = "CARGO_FEATURE_";
49         const CARGO_CFG_PREFIX: &str = "CARGO_CFG_";
50 
51         let mut features = Set::new();
52         let mut cfgs = Map::new();
53         for (k, v) in env::vars_os() {
54             let k = match k.to_str() {
55                 Some(k) => k,
56                 None => continue,
57             };
58             let v = match v.into_string() {
59                 Ok(v) => v,
60                 Err(_) => continue,
61             };
62             if let Some(feature_name) = k.strip_prefix(CARGO_FEATURE_PREFIX) {
63                 let feature_name = Name(feature_name.to_owned());
64                 features.insert(feature_name);
65             } else if let Some(cfg_name) = k.strip_prefix(CARGO_CFG_PREFIX) {
66                 let cfg_name = Name(cfg_name.to_owned());
67                 cfgs.insert(cfg_name, v);
68             }
69         }
70         CargoEnv { features, cfgs }
71     }
72 }
73 
74 struct Name(String);
75 
76 impl Ord for Name {
cmp(&self, rhs: &Self) -> Ordering77     fn cmp(&self, rhs: &Self) -> Ordering {
78         Lookup::new(&self.0).cmp(Lookup::new(&rhs.0))
79     }
80 }
81 
82 impl PartialOrd for Name {
partial_cmp(&self, rhs: &Self) -> Option<Ordering>83     fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
84         Some(self.cmp(rhs))
85     }
86 }
87 
88 impl Eq for Name {}
89 
90 impl PartialEq for Name {
eq(&self, rhs: &Self) -> bool91     fn eq(&self, rhs: &Self) -> bool {
92         Lookup::new(&self.0).eq(Lookup::new(&rhs.0))
93     }
94 }
95 
96 #[repr(transparent)]
97 struct Lookup(str);
98 
99 impl Lookup {
new(name: &str) -> &Self100     fn new(name: &str) -> &Self {
101         unsafe { &*(name as *const str as *const Self) }
102     }
103 }
104 
105 impl Borrow<Lookup> for Name {
borrow(&self) -> &Lookup106     fn borrow(&self) -> &Lookup {
107         Lookup::new(&self.0)
108     }
109 }
110 
111 impl Ord for Lookup {
cmp(&self, rhs: &Self) -> Ordering112     fn cmp(&self, rhs: &Self) -> Ordering {
113         self.0
114             .bytes()
115             .map(CaseAgnosticByte)
116             .cmp(rhs.0.bytes().map(CaseAgnosticByte))
117     }
118 }
119 
120 impl PartialOrd for Lookup {
partial_cmp(&self, rhs: &Self) -> Option<Ordering>121     fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
122         Some(self.cmp(rhs))
123     }
124 }
125 
126 impl Eq for Lookup {}
127 
128 impl PartialEq for Lookup {
eq(&self, rhs: &Self) -> bool129     fn eq(&self, rhs: &Self) -> bool {
130         self.0
131             .bytes()
132             .map(CaseAgnosticByte)
133             .eq(rhs.0.bytes().map(CaseAgnosticByte))
134     }
135 }
136 
137 struct CaseAgnosticByte(u8);
138 
139 impl Ord for CaseAgnosticByte {
cmp(&self, rhs: &Self) -> Ordering140     fn cmp(&self, rhs: &Self) -> Ordering {
141         self.0.to_ascii_lowercase().cmp(&rhs.0.to_ascii_lowercase())
142     }
143 }
144 
145 impl PartialOrd for CaseAgnosticByte {
partial_cmp(&self, rhs: &Self) -> Option<Ordering>146     fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
147         Some(self.cmp(rhs))
148     }
149 }
150 
151 impl Eq for CaseAgnosticByte {}
152 
153 impl PartialEq for CaseAgnosticByte {
eq(&self, rhs: &Self) -> bool154     fn eq(&self, rhs: &Self) -> bool {
155         self.cmp(rhs) == Ordering::Equal
156     }
157 }
158