1 | //! Platform definition used by Cargo. |
2 | //! |
3 | //! This defines a [`Platform`] type which is used in Cargo to specify a target platform. |
4 | //! There are two kinds, a named target like `x86_64-apple-darwin`, and a "cfg expression" |
5 | //! like `cfg(any(target_os = "macos", target_os = "ios"))`. |
6 | //! |
7 | //! See `examples/matches.rs` for an example of how to match against a `Platform`. |
8 | //! |
9 | //! > This crate is maintained by the Cargo team for use by the wider |
10 | //! > ecosystem. This crate follows semver compatibility for its APIs. |
11 | //! |
12 | //! [`Platform`]: enum.Platform.html |
13 | |
14 | use std::fmt; |
15 | use std::str::FromStr; |
16 | |
17 | mod cfg; |
18 | mod error; |
19 | |
20 | pub use cfg::{Cfg, CfgExpr}; |
21 | pub use error::{ParseError, ParseErrorKind}; |
22 | |
23 | /// Platform definition. |
24 | #[derive (Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)] |
25 | pub enum Platform { |
26 | /// A named platform, like `x86_64-apple-darwin`. |
27 | Name(String), |
28 | /// A cfg expression, like `cfg(windows)`. |
29 | Cfg(CfgExpr), |
30 | } |
31 | |
32 | impl Platform { |
33 | /// Returns whether the Platform matches the given target and cfg. |
34 | /// |
35 | /// The named target and cfg values should be obtained from `rustc`. |
36 | pub fn matches(&self, name: &str, cfg: &[Cfg]) -> bool { |
37 | match *self { |
38 | Platform::Name(ref p) => p == name, |
39 | Platform::Cfg(ref p) => p.matches(cfg), |
40 | } |
41 | } |
42 | |
43 | fn validate_named_platform(name: &str) -> Result<(), ParseError> { |
44 | if let Some(ch) = name |
45 | .chars() |
46 | .find(|&c| !(c.is_alphanumeric() || c == '_' || c == '-' || c == '.' )) |
47 | { |
48 | if name.chars().any(|c| c == '(' ) { |
49 | return Err(ParseError::new( |
50 | name, |
51 | ParseErrorKind::InvalidTarget( |
52 | "unexpected `(` character, cfg expressions must start with `cfg(`" |
53 | .to_string(), |
54 | ), |
55 | )); |
56 | } |
57 | return Err(ParseError::new( |
58 | name, |
59 | ParseErrorKind::InvalidTarget(format!( |
60 | "unexpected character {} in target name" , |
61 | ch |
62 | )), |
63 | )); |
64 | } |
65 | Ok(()) |
66 | } |
67 | |
68 | pub fn check_cfg_attributes(&self, warnings: &mut Vec<String>) { |
69 | fn check_cfg_expr(expr: &CfgExpr, warnings: &mut Vec<String>) { |
70 | match *expr { |
71 | CfgExpr::Not(ref e) => check_cfg_expr(e, warnings), |
72 | CfgExpr::All(ref e) | CfgExpr::Any(ref e) => { |
73 | for e in e { |
74 | check_cfg_expr(e, warnings); |
75 | } |
76 | } |
77 | CfgExpr::Value(ref e) => match e { |
78 | Cfg::Name(name) => match name.as_str() { |
79 | "test" | "debug_assertions" | "proc_macro" => |
80 | warnings.push(format!( |
81 | "Found ` {}` in `target.'cfg(...)'.dependencies`. \ |
82 | This value is not supported for selecting dependencies \ |
83 | and will not work as expected. \ |
84 | To learn more visit \ |
85 | https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies" , |
86 | name |
87 | )), |
88 | _ => (), |
89 | }, |
90 | Cfg::KeyPair(name, _) => if name.as_str() == "feature" { |
91 | warnings.push(String::from( |
92 | "Found `feature = ...` in `target.'cfg(...)'.dependencies`. \ |
93 | This key is not supported for selecting dependencies \ |
94 | and will not work as expected. \ |
95 | Use the [features] section instead: \ |
96 | https://doc.rust-lang.org/cargo/reference/features.html" |
97 | )) |
98 | }, |
99 | } |
100 | } |
101 | } |
102 | |
103 | if let Platform::Cfg(cfg) = self { |
104 | check_cfg_expr(cfg, warnings); |
105 | } |
106 | } |
107 | } |
108 | |
109 | impl serde::Serialize for Platform { |
110 | fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error> |
111 | where |
112 | S: serde::Serializer, |
113 | { |
114 | self.to_string().serialize(serializer:s) |
115 | } |
116 | } |
117 | |
118 | impl<'de> serde::Deserialize<'de> for Platform { |
119 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
120 | where |
121 | D: serde::Deserializer<'de>, |
122 | { |
123 | let s: String = String::deserialize(deserializer)?; |
124 | FromStr::from_str(&s).map_err(op:serde::de::Error::custom) |
125 | } |
126 | } |
127 | |
128 | impl FromStr for Platform { |
129 | type Err = ParseError; |
130 | |
131 | fn from_str(s: &str) -> Result<Platform, ParseError> { |
132 | if let Some(s: &str) = s.strip_prefix("cfg(" ).and_then(|s: &str| s.strip_suffix(')' )) { |
133 | s.parse().map(op:Platform::Cfg) |
134 | } else { |
135 | Platform::validate_named_platform(name:s)?; |
136 | Ok(Platform::Name(s.to_string())) |
137 | } |
138 | } |
139 | } |
140 | |
141 | impl fmt::Display for Platform { |
142 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
143 | match *self { |
144 | Platform::Name(ref n: &String) => n.fmt(f), |
145 | Platform::Cfg(ref e: &CfgExpr) => write!(f, "cfg( {})" , e), |
146 | } |
147 | } |
148 | } |
149 | |