Skip to content

Commit 32693c9

Browse files
committed
Add 'supports_feature()'.
1 parent 7d81cbd commit 32693c9

File tree

1 file changed

+101
-39
lines changed

1 file changed

+101
-39
lines changed

src/lib.rs

+101-39
Original file line numberDiff line numberDiff line change
@@ -6,58 +6,68 @@
66
//!
77
//! # Examples
88
//!
9-
//! Set a `cfg` flag in `build.rs` if the running compiler was determined to be
10-
//! at least version `1.13.0`:
9+
//! * Set a `cfg` flag in `build.rs` if the running compiler was determined to
10+
//! be at least version `1.13.0`:
1111
//!
12-
//! ```rust
13-
//! extern crate version_check as rustc;
12+
//! ```rust
13+
//! extern crate version_check as rustc;
1414
//!
15-
//! if rustc::is_min_version("1.13.0").unwrap_or(false) {
16-
//! println!("cargo:rustc-cfg=question_mark_operator");
17-
//! }
18-
//! ```
15+
//! if rustc::is_min_version("1.13.0").unwrap_or(false) {
16+
//! println!("cargo:rustc-cfg=question_mark_operator");
17+
//! }
18+
//! ```
1919
//!
20-
//! See [`is_max_version`] or [`is_exact_version`] to check if the compiler
21-
//! is _at most_ or _exactly_ a certain version.
20+
//! See [`is_max_version`] or [`is_exact_version`] to check if the compiler
21+
//! is _at most_ or _exactly_ a certain version.
2222
//!
23-
//! Check that the running compiler was released on or after `2018-12-18`:
23+
//! * Check that the running compiler was released on or after `2018-12-18`:
2424
//!
25-
//! ```rust
26-
//! extern crate version_check as rustc;
25+
//! ```rust
26+
//! extern crate version_check as rustc;
2727
//!
28-
//! match rustc::is_min_date("2018-12-18") {
29-
//! Some(true) => "Yep! It's recent!",
30-
//! Some(false) => "No, it's older.",
31-
//! None => "Couldn't determine the rustc version."
32-
//! };
33-
//! ```
28+
//! match rustc::is_min_date("2018-12-18") {
29+
//! Some(true) => "Yep! It's recent!",
30+
//! Some(false) => "No, it's older.",
31+
//! None => "Couldn't determine the rustc version."
32+
//! };
33+
//! ```
3434
//!
35-
//! See [`is_max_date`] or [`is_exact_date`] to check if the compiler was
36-
//! released _prior to_ or _exactly on_ a certain date.
35+
//! See [`is_max_date`] or [`is_exact_date`] to check if the compiler was
36+
//! released _prior to_ or _exactly on_ a certain date.
3737
//!
38-
//! Check that the running compiler supports feature flags:
38+
//! * Check that the running compiler supports feature flags:
3939
//!
40-
//! ```rust
41-
//! extern crate version_check as rustc;
40+
//! ```rust
41+
//! extern crate version_check as rustc;
4242
//!
43-
//! match rustc::is_feature_flaggable() {
44-
//! Some(true) => "Yes! It's a dev or nightly release!",
45-
//! Some(false) => "No, it's stable or beta.",
46-
//! None => "Couldn't determine the rustc version."
47-
//! };
48-
//! ```
43+
//! match rustc::is_feature_flaggable() {
44+
//! Some(true) => "Yes! It's a dev or nightly release!",
45+
//! Some(false) => "No, it's stable or beta.",
46+
//! None => "Couldn't determine the rustc version."
47+
//! };
48+
//! ```
4949
//!
50-
//! Check that the running compiler is on the stable channel:
50+
//! * Check that the running compiler supports a specific feature:
5151
//!
52-
//! ```rust
53-
//! extern crate version_check as rustc;
52+
//! ```rust
53+
//! extern crate version_check as rustc;
5454
//!
55-
//! match rustc::Channel::read() {
56-
//! Some(c) if c.is_stable() => format!("Yes! It's stable."),
57-
//! Some(c) => format!("No, the channel {} is not stable.", c),
58-
//! None => format!("Couldn't determine the rustc version.")
59-
//! };
60-
//! ```
55+
//! if let Some(true) = rustc::supports_feature("doc_cfg") {
56+
//! println!("cargo:rustc-cfg=has_doc_cfg");
57+
//! }
58+
//! ```
59+
//!
60+
//! * Check that the running compiler is on the stable channel:
61+
//!
62+
//! ```rust
63+
//! extern crate version_check as rustc;
64+
//!
65+
//! match rustc::Channel::read() {
66+
//! Some(c) if c.is_stable() => format!("Yes! It's stable."),
67+
//! Some(c) => format!("No, the channel {} is not stable.", c),
68+
//! None => format!("Couldn't determine the rustc version.")
69+
//! };
70+
//! ```
6171
//!
6272
//! To interact with the version, release date, and release channel as structs,
6373
//! use [`Version`], [`Date`], and [`Channel`], respectively. The [`triple()`]
@@ -248,12 +258,64 @@ pub fn is_exact_version(version: &str) -> Option<bool> {
248258
///
249259
/// In other words, if the channel is either "nightly" or "dev".
250260
///
261+
/// Note that support for specific `rustc` features can be enabled or disabled
262+
/// via the `allow-features` compiler flag, which this function _does not_
263+
/// check. That is, this function _does not_ check whether a _specific_ feature
264+
/// is supported, but instead whether features are supported at all. To check
265+
/// for support for a specific feature, use [`supports_feature()`].
266+
///
251267
/// If the version could not be determined, returns `None`. Otherwise returns
252268
/// `true` if the running version supports feature flags and `false` otherwise.
253269
pub fn is_feature_flaggable() -> Option<bool> {
254270
Channel::read().map(|c| c.supports_features())
255271
}
256272

273+
/// Checks whether the running or installed `rustc` supports `feature`.
274+
///
275+
/// Returns _true_ _iff_ [`is_feature_flaggable()`] returns `true` _and_ the
276+
/// feature is not disabled via exclusion in `allow-features` via `RUSTFLAGS` or
277+
/// `CARGO_ENCODED_RUSTFLAGS`. If the version could not be determined, returns
278+
/// `None`.
279+
///
280+
/// # Example
281+
///
282+
/// ```rust
283+
/// use version_check as rustc;
284+
///
285+
/// if let Some(true) = rustc::supports_feature("doc_cfg") {
286+
/// println!("cargo:rustc-cfg=has_doc_cfg");
287+
/// }
288+
/// ```
289+
pub fn supports_feature(feature: &str) -> Option<bool> {
290+
match is_feature_flaggable() {
291+
Some(true) => { /* continue */ }
292+
Some(false) => return Some(false),
293+
None => return None,
294+
}
295+
296+
let env_flags = env::var_os("CARGO_ENCODED_RUSTFLAGS")
297+
.map(|flags| (flags, '\x1f'))
298+
.or_else(|| env::var_os("RUSTFLAGS").map(|flags| (flags, ' ')));
299+
300+
if let Some((flags, delim)) = env_flags {
301+
const ALLOW_FEATURES: &'static str = "allow-features=";
302+
303+
let rustflags = flags.to_string_lossy();
304+
let allow_features = rustflags.split(delim)
305+
.map(|flag| flag.trim_left_matches("-Z").trim())
306+
.filter(|flag| flag.starts_with(ALLOW_FEATURES))
307+
.map(|flag| &flag[ALLOW_FEATURES.len()..]);
308+
309+
if let Some(allow_features) = allow_features.last() {
310+
return Some(allow_features.split(',').any(|f| f.trim() == feature));
311+
}
312+
}
313+
314+
// If there are no `RUSTFLAGS` or `CARGO_ENCODED_RUSTFLAGS` or they don't
315+
// contain an `allow-features` flag, assume compiler allows all features.
316+
Some(true)
317+
}
318+
257319
#[cfg(test)]
258320
mod tests {
259321
use std::{env, fs};

0 commit comments

Comments
 (0)