Skip to content

Commit d9ed1ed

Browse files
committed
Add CheckCfg/ExpectedValues to cargo-platform
1 parent c7d6ffc commit d9ed1ed

File tree

3 files changed

+223
-0
lines changed

3 files changed

+223
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//! check-cfg
2+
3+
use std::{
4+
collections::{HashMap, HashSet},
5+
error::Error,
6+
fmt::Display,
7+
};
8+
9+
/// Check Config (aka `--check-cfg`/`--print=check-cfg` representation)
10+
#[derive(Debug, Default, Clone)]
11+
pub struct CheckCfg {
12+
/// Is `--check-cfg` activated
13+
pub exhaustive: bool,
14+
/// List of expected cfgs
15+
pub expecteds: HashMap<String, ExpectedValues>,
16+
}
17+
18+
/// List of expected check-cfg values
19+
#[derive(Debug, Clone, PartialEq, Eq)]
20+
pub enum ExpectedValues {
21+
/// List of expected values
22+
///
23+
/// - `#[cfg(foo)]` value is `None`
24+
/// - `#[cfg(foo = "")]` value is `Some("")`
25+
/// - `#[cfg(foo = "bar")]` value is `Some("bar")`
26+
Some(HashSet<Option<String>>),
27+
/// All values expected
28+
Any,
29+
}
30+
31+
/// Error when parse a line from `--print=check-cfg`
32+
#[derive(Debug)]
33+
#[non_exhaustive]
34+
pub struct PrintCheckCfgParsingError;
35+
36+
impl Error for PrintCheckCfgParsingError {}
37+
38+
impl Display for PrintCheckCfgParsingError {
39+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40+
f.write_str("error when parsing a `--print=check-cfg` line")
41+
}
42+
}
43+
44+
impl CheckCfg {
45+
/// Parse a line from `--print=check-cfg`
46+
pub fn parse_print_check_cfg_line(
47+
&mut self,
48+
line: &str,
49+
) -> Result<(), PrintCheckCfgParsingError> {
50+
if line == "any()=any()" || line == "any()" {
51+
self.exhaustive = false;
52+
return Ok(());
53+
}
54+
55+
let mut value: HashSet<Option<String>> = HashSet::default();
56+
let mut value_any_specified = false;
57+
let name: String;
58+
59+
if let Some((n, val)) = line.split_once('=') {
60+
name = n.to_string();
61+
62+
if val == "any()" {
63+
value_any_specified = true;
64+
} else if val.is_empty() {
65+
// no value, nothing to add
66+
} else if let Some(val) = maybe_quoted_value(val) {
67+
value.insert(Some(val.to_string()));
68+
} else {
69+
// missing quotes and non-empty
70+
return Err(PrintCheckCfgParsingError);
71+
}
72+
} else {
73+
name = line.to_string();
74+
value.insert(None);
75+
}
76+
77+
self.expecteds
78+
.entry(name)
79+
.and_modify(|v| match v {
80+
ExpectedValues::Some(_) if value_any_specified => *v = ExpectedValues::Any,
81+
ExpectedValues::Some(v) => v.extend(value.clone()),
82+
ExpectedValues::Any => {}
83+
})
84+
.or_insert_with(|| {
85+
if value_any_specified {
86+
ExpectedValues::Any
87+
} else {
88+
ExpectedValues::Some(value)
89+
}
90+
});
91+
Ok(())
92+
}
93+
}
94+
95+
fn maybe_quoted_value<'a>(v: &'a str) -> Option<&'a str> {
96+
// strip "" around the value, e.g. "linux" -> linux
97+
v.strip_prefix('"').and_then(|v| v.strip_suffix('"'))
98+
}

crates/cargo-platform/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ use std::str::FromStr;
1515
use std::{fmt, path::Path};
1616

1717
mod cfg;
18+
mod check_cfg;
1819
mod error;
1920

2021
use cfg::KEYWORDS;
2122
pub use cfg::{Cfg, CfgExpr, Ident};
23+
pub use check_cfg::{CheckCfg, ExpectedValues};
2224
pub use error::{ParseError, ParseErrorKind};
2325

2426
/// Platform definition.
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use cargo_platform::{CheckCfg, ExpectedValues};
2+
use std::collections::HashSet;
3+
4+
#[test]
5+
fn print_check_cfg_none() {
6+
let mut check_cfg = CheckCfg::default();
7+
8+
check_cfg.parse_print_check_cfg_line("cfg_a").unwrap();
9+
assert_eq!(
10+
*check_cfg.expecteds.get("cfg_a").unwrap(),
11+
ExpectedValues::Some(HashSet::from([None]))
12+
);
13+
}
14+
15+
#[test]
16+
fn print_check_cfg_empty() {
17+
let mut check_cfg = CheckCfg::default();
18+
19+
check_cfg.parse_print_check_cfg_line("cfg_b=").unwrap();
20+
assert_eq!(
21+
*check_cfg.expecteds.get("cfg_b").unwrap(),
22+
ExpectedValues::Some(HashSet::from([]))
23+
);
24+
}
25+
26+
#[test]
27+
fn print_check_cfg_any() {
28+
let mut check_cfg = CheckCfg::default();
29+
30+
check_cfg.parse_print_check_cfg_line("cfg_c=any()").unwrap();
31+
assert_eq!(
32+
*check_cfg.expecteds.get("cfg_c").unwrap(),
33+
ExpectedValues::Any
34+
);
35+
36+
check_cfg.parse_print_check_cfg_line("cfg_c").unwrap();
37+
assert_eq!(
38+
*check_cfg.expecteds.get("cfg_c").unwrap(),
39+
ExpectedValues::Any
40+
);
41+
}
42+
43+
#[test]
44+
fn print_check_cfg_value() {
45+
let mut check_cfg = CheckCfg::default();
46+
47+
check_cfg
48+
.parse_print_check_cfg_line("cfg_d=\"test\"")
49+
.unwrap();
50+
assert_eq!(
51+
*check_cfg.expecteds.get("cfg_d").unwrap(),
52+
ExpectedValues::Some(HashSet::from([Some("test".to_string())]))
53+
);
54+
55+
check_cfg
56+
.parse_print_check_cfg_line("cfg_d=\"tmp\"")
57+
.unwrap();
58+
assert_eq!(
59+
*check_cfg.expecteds.get("cfg_d").unwrap(),
60+
ExpectedValues::Some(HashSet::from([
61+
Some("test".to_string()),
62+
Some("tmp".to_string())
63+
]))
64+
);
65+
}
66+
67+
#[test]
68+
fn print_check_cfg_none_and_value() {
69+
let mut check_cfg = CheckCfg::default();
70+
71+
check_cfg.parse_print_check_cfg_line("cfg").unwrap();
72+
check_cfg.parse_print_check_cfg_line("cfg=\"foo\"").unwrap();
73+
assert_eq!(
74+
*check_cfg.expecteds.get("cfg").unwrap(),
75+
ExpectedValues::Some(HashSet::from([None, Some("foo".to_string())]))
76+
);
77+
}
78+
79+
#[test]
80+
fn print_check_cfg_quote_in_value() {
81+
let mut check_cfg = CheckCfg::default();
82+
83+
check_cfg
84+
.parse_print_check_cfg_line("cfg_e=\"quote_in_value\"\"")
85+
.unwrap();
86+
assert_eq!(
87+
*check_cfg.expecteds.get("cfg_e").unwrap(),
88+
ExpectedValues::Some(HashSet::from([Some("quote_in_value\"".to_string())]))
89+
);
90+
}
91+
92+
#[test]
93+
fn print_check_cfg_value_and_any() {
94+
let mut check_cfg = CheckCfg::default();
95+
96+
// having both a value and `any()` shouldn't be possible but better
97+
// handle this correctly anyway
98+
99+
check_cfg
100+
.parse_print_check_cfg_line("cfg_1=\"foo\"")
101+
.unwrap();
102+
check_cfg.parse_print_check_cfg_line("cfg_1=any()").unwrap();
103+
assert_eq!(
104+
*check_cfg.expecteds.get("cfg_1").unwrap(),
105+
ExpectedValues::Any
106+
);
107+
108+
check_cfg.parse_print_check_cfg_line("cfg_2=any()").unwrap();
109+
check_cfg
110+
.parse_print_check_cfg_line("cfg_2=\"foo\"")
111+
.unwrap();
112+
assert_eq!(
113+
*check_cfg.expecteds.get("cfg_2").unwrap(),
114+
ExpectedValues::Any
115+
);
116+
}
117+
118+
#[test]
119+
#[should_panic]
120+
fn print_check_cfg_missing_quote_value() {
121+
let mut check_cfg = CheckCfg::default();
122+
check_cfg.parse_print_check_cfg_line("foo=bar").unwrap();
123+
}

0 commit comments

Comments
 (0)