mirror of
https://gitee.com/openharmony/third_party_rust_version_check
synced 2024-11-23 07:49:54 +00:00
New version: 0.9.
This commit is contained in:
parent
96a6d6ccbe
commit
98871bdc8e
6
.travis.yml
Normal file
6
.travis.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
language: rust
|
||||||
|
rust:
|
||||||
|
- 1.0.0
|
||||||
|
- stable
|
||||||
|
- beta
|
||||||
|
- nightly
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.1.5"
|
version = "0.9.0"
|
||||||
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
||||||
description = "Tiny crate to check the version of the installed/running rustc."
|
description = "Tiny crate to check the version of the installed/running rustc."
|
||||||
documentation = "https://docs.rs/version_check/"
|
documentation = "https://docs.rs/version_check/"
|
||||||
|
61
README.md
61
README.md
@ -1,5 +1,9 @@
|
|||||||
# version\_check
|
# version\_check
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.com/SergioBenitez/version_check.svg?branch=master)](https://travis-ci.com/SergioBenitez/version_check)
|
||||||
|
[![Current Crates.io Version](https://img.shields.io/crates/v/version_check.svg)](https://crates.io/crates/version_check)
|
||||||
|
[![rustdocs on docs.rs](https://docs.rs/version_check/badge.svg)](https://docs.rs/version_check)
|
||||||
|
|
||||||
This tiny crate checks that the running or installed `rustc` meets some version
|
This tiny crate checks that the running or installed `rustc` meets some version
|
||||||
requirements. The version is queried by calling the Rust compiler with
|
requirements. The version is queried by calling the Rust compiler with
|
||||||
`--version`. The path to the compiler is determined first via the `RUSTC`
|
`--version`. The path to the compiler is determined first via the `RUSTC`
|
||||||
@ -12,54 +16,57 @@ Add to your `Cargo.toml` file, typically as a build dependency:
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
version_check = "0.1"
|
version_check = "0.9"
|
||||||
```
|
```
|
||||||
|
|
||||||
`version_check` is compatible and compiles with Rust 1.0.0 and beyond.
|
`version_check` is compatible and compiles with Rust 1.0.0 and beyond.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
Check that the running compiler is a nightly release:
|
Set a `cfg` flag in `build.rs` if the running compiler was determined to be
|
||||||
|
at least version `1.13.0`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
extern crate version_check;
|
extern crate version_check as rustc;
|
||||||
|
|
||||||
match version_check::is_nightly() {
|
if rustc::is_min_version("1.13.0").unwrap_or(false) {
|
||||||
Some(true) => "running a nightly",
|
println!("cargo:rustc-cfg=question_mark_operator");
|
||||||
Some(false) => "not nightly",
|
}
|
||||||
None => "couldn't figure it out"
|
```
|
||||||
|
|
||||||
|
Check that the running compiler was released on or after `2018-12-18`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
extern crate version_check as rustc;
|
||||||
|
|
||||||
|
match rustc::is_min_date("2018-12-18") {
|
||||||
|
Some(true) => "Yep! It's recent!",
|
||||||
|
Some(false) => "No, it's older.",
|
||||||
|
None => "Couldn't determine the rustc version."
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Check that the running compiler is at least version `1.13.0`:
|
Check that the running compiler supports feature flags:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
extern crate version_check;
|
extern crate version_check as rustc;
|
||||||
|
|
||||||
match version_check::is_min_version("1.13.0") {
|
match rustc::is_feature_flaggable() {
|
||||||
Some((true, version)) => format!("Yes! It's: {}", version),
|
Some(true) => "Yes! It's a dev or nightly release!",
|
||||||
Some((false, version)) => format!("No! {} is too old!", version),
|
Some(false) => "No, it's stable or beta.",
|
||||||
None => "couldn't figure it out".into()
|
None => "Couldn't determine the rustc version."
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Check that the running compiler was released on or after `2016-12-18`:
|
See the [rustdocs](https://docs.rs/version_check) for more examples and complete
|
||||||
|
documentation.
|
||||||
```rust
|
|
||||||
extern crate version_check;
|
|
||||||
|
|
||||||
match version_check::is_min_date("2016-12-18") {
|
|
||||||
Some((true, date)) => format!("Yes! It's: {}", date),
|
|
||||||
Some((false, date)) => format!("No! {} is too long ago!", date),
|
|
||||||
None => "couldn't figure it out".into()
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Alternatives
|
## Alternatives
|
||||||
|
|
||||||
This crate is dead simple with no dependencies. If you need something more and
|
This crate is dead simple with no dependencies. If you need something more
|
||||||
don't care about panicking if the version cannot be obtained or adding
|
and don't care about panicking if the version cannot be obtained, or if you
|
||||||
dependencies, see [rustc_version](https://crates.io/crates/rustc_version).
|
don't mind adding dependencies, see
|
||||||
|
[rustc_version](https://crates.io/crates/rustc_version).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
192
src/channel.rs
Normal file
192
src/channel.rs
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
enum Kind {
|
||||||
|
Dev,
|
||||||
|
Nightly,
|
||||||
|
Beta,
|
||||||
|
Stable,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Release channel: "dev", "nightly", "beta", or "stable".
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub struct Channel(Kind);
|
||||||
|
|
||||||
|
impl Channel {
|
||||||
|
/// Reads the release channel of the running compiler. If it cannot be
|
||||||
|
/// determined (see the [top-level documentation](crate)), returns `None`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Channel;
|
||||||
|
///
|
||||||
|
/// match Channel::read() {
|
||||||
|
/// Some(c) => format!("The channel is: {}", c),
|
||||||
|
/// None => format!("Failed to read the release channel.")
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
pub fn read() -> Option<Channel> {
|
||||||
|
::get_version_and_date()
|
||||||
|
.and_then(|(version, _)| version)
|
||||||
|
.and_then(|version| Channel::parse(&version))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a Rust release channel from a Rust release version string (of the
|
||||||
|
/// form `major[.minor[.patch[-channel]]]`). Returns `None` if `version` is
|
||||||
|
/// not a valid Rust version string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Channel;
|
||||||
|
///
|
||||||
|
/// let dev = Channel::parse("1.3.0-dev").unwrap();
|
||||||
|
/// assert!(dev.is_dev());
|
||||||
|
///
|
||||||
|
/// let nightly = Channel::parse("1.42.2-nightly").unwrap();
|
||||||
|
/// assert!(nightly.is_nightly());
|
||||||
|
///
|
||||||
|
/// let beta = Channel::parse("1.32.0-beta").unwrap();
|
||||||
|
/// assert!(beta.is_beta());
|
||||||
|
///
|
||||||
|
/// let stable = Channel::parse("1.4.0").unwrap();
|
||||||
|
/// assert!(stable.is_stable());
|
||||||
|
/// ```
|
||||||
|
pub fn parse(version: &str) -> Option<Channel> {
|
||||||
|
if version.contains("-dev") {
|
||||||
|
Some(Channel(Kind::Dev))
|
||||||
|
} else if version.contains("-nightly") {
|
||||||
|
Some(Channel(Kind::Nightly))
|
||||||
|
} else if version.contains("-beta") {
|
||||||
|
Some(Channel(Kind::Beta))
|
||||||
|
} else if !version.contains("-") {
|
||||||
|
Some(Channel(Kind::Stable))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the name of the release channel.
|
||||||
|
fn as_str(&self) -> &'static str {
|
||||||
|
match self.0 {
|
||||||
|
Kind::Dev => "dev",
|
||||||
|
Kind::Beta => "beta",
|
||||||
|
Kind::Nightly => "nightly",
|
||||||
|
Kind::Stable => "stable",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this channel supports feature flags. In other words,
|
||||||
|
/// returns `true` if the channel is either `dev` or `nightly`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Channel;
|
||||||
|
///
|
||||||
|
/// let dev = Channel::parse("1.3.0-dev").unwrap();
|
||||||
|
/// assert!(dev.supports_features());
|
||||||
|
///
|
||||||
|
/// let nightly = Channel::parse("1.42.2-nightly").unwrap();
|
||||||
|
/// assert!(nightly.supports_features());
|
||||||
|
///
|
||||||
|
/// let beta = Channel::parse("1.32.0-beta").unwrap();
|
||||||
|
/// assert!(!beta.supports_features());
|
||||||
|
///
|
||||||
|
/// let stable = Channel::parse("1.4.0").unwrap();
|
||||||
|
/// assert!(!stable.supports_features());
|
||||||
|
/// ```
|
||||||
|
pub fn supports_features(&self) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
Kind::Dev | Kind::Nightly => true,
|
||||||
|
Kind::Beta | Kind::Stable => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this channel is `dev` and `false` otherwise.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Channel;
|
||||||
|
///
|
||||||
|
/// let dev = Channel::parse("1.3.0-dev").unwrap();
|
||||||
|
/// assert!(dev.is_dev());
|
||||||
|
///
|
||||||
|
/// let stable = Channel::parse("1.0.0").unwrap();
|
||||||
|
/// assert!(!stable.is_dev());
|
||||||
|
/// ```
|
||||||
|
pub fn is_dev(&self) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
Kind::Dev => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this channel is `nightly` and `false` otherwise.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Channel;
|
||||||
|
///
|
||||||
|
/// let nightly = Channel::parse("1.3.0-nightly").unwrap();
|
||||||
|
/// assert!(nightly.is_nightly());
|
||||||
|
///
|
||||||
|
/// let stable = Channel::parse("1.0.0").unwrap();
|
||||||
|
/// assert!(!stable.is_nightly());
|
||||||
|
/// ```
|
||||||
|
pub fn is_nightly(&self) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
Kind::Nightly => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this channel is `beta` and `false` otherwise.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Channel;
|
||||||
|
///
|
||||||
|
/// let beta = Channel::parse("1.3.0-beta").unwrap();
|
||||||
|
/// assert!(beta.is_beta());
|
||||||
|
///
|
||||||
|
/// let stable = Channel::parse("1.0.0").unwrap();
|
||||||
|
/// assert!(!stable.is_beta());
|
||||||
|
/// ```
|
||||||
|
pub fn is_beta(&self) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
Kind::Beta => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this channel is `stable` and `false` otherwise.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Channel;
|
||||||
|
///
|
||||||
|
/// let stable = Channel::parse("1.0.0").unwrap();
|
||||||
|
/// assert!(stable.is_stable());
|
||||||
|
///
|
||||||
|
/// let beta = Channel::parse("1.3.0-beta").unwrap();
|
||||||
|
/// assert!(!beta.is_stable());
|
||||||
|
/// ```
|
||||||
|
pub fn is_stable(&self) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
Kind::Stable => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Channel {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.as_str())
|
||||||
|
}
|
||||||
|
}
|
146
src/date.rs
Normal file
146
src/date.rs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// Release date including year, month, and day.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
|
||||||
|
pub struct Date(u32);
|
||||||
|
|
||||||
|
impl Date {
|
||||||
|
/// Reads the release date of the running compiler. If it cannot be
|
||||||
|
/// determined (see the [top-level documentation](crate)), returns `None`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Date;
|
||||||
|
///
|
||||||
|
/// match Date::read() {
|
||||||
|
/// Some(d) => format!("The release date is: {}", d),
|
||||||
|
/// None => format!("Failed to read the release date.")
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
pub fn read() -> Option<Date> {
|
||||||
|
::get_version_and_date()
|
||||||
|
.and_then(|(_, date)| date)
|
||||||
|
.and_then(|date| Date::parse(&date))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the original (YYYY, MM, DD).
|
||||||
|
fn to_ymd(&self) -> (u8, u8, u8) {
|
||||||
|
let y = self.0 >> 9;
|
||||||
|
let m = (self.0 << 23) >> 28;
|
||||||
|
let d = (self.0 << 27) >> 27;
|
||||||
|
(y as u8, m as u8, d as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a release date of the form `%Y-%m-%d`. Returns `None` if `date` is
|
||||||
|
/// not in `%Y-%m-%d` format.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Date;
|
||||||
|
///
|
||||||
|
/// let date = Date::parse("2016-04-20").unwrap();
|
||||||
|
///
|
||||||
|
/// assert!(date.at_least("2016-01-10"));
|
||||||
|
/// assert!(date.at_most("2016-04-20"));
|
||||||
|
/// assert!(date.exactly("2016-04-20"));
|
||||||
|
///
|
||||||
|
/// assert!(Date::parse("March 13, 2018").is_none());
|
||||||
|
/// assert!(Date::parse("1-2-3-4-5").is_none());
|
||||||
|
/// ```
|
||||||
|
pub fn parse(date: &str) -> Option<Date> {
|
||||||
|
let ymd: Vec<u32> = date.split("-")
|
||||||
|
.filter_map(|s| s.parse::<u32>().ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if ymd.len() != 3 {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
let (y, m, d) = (ymd[0], ymd[1], ymd[2]);
|
||||||
|
Some(Date((y << 9) | (m << 5) | d))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if `self` occurs on or after `date`.
|
||||||
|
///
|
||||||
|
/// If `date` occurs before `self`, or if `date` is not in `%Y-%m-%d`
|
||||||
|
/// format, returns `false`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Date;
|
||||||
|
///
|
||||||
|
/// let date = Date::parse("2020-01-01").unwrap();
|
||||||
|
///
|
||||||
|
/// assert!(date.at_least("2019-12-31"));
|
||||||
|
/// assert!(date.at_least("2020-01-01"));
|
||||||
|
/// assert!(date.at_least("2014-04-31"));
|
||||||
|
///
|
||||||
|
/// assert!(!date.at_least("2020-01-02"));
|
||||||
|
/// assert!(!date.at_least("2024-08-18"));
|
||||||
|
/// ```
|
||||||
|
pub fn at_least(&self, date: &str) -> bool {
|
||||||
|
Date::parse(date)
|
||||||
|
.map(|date| self >= &date)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if `self` occurs on or before `date`.
|
||||||
|
///
|
||||||
|
/// If `date` occurs after `self`, or if `date` is not in `%Y-%m-%d`
|
||||||
|
/// format, returns `false`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Date;
|
||||||
|
///
|
||||||
|
/// let date = Date::parse("2020-01-01").unwrap();
|
||||||
|
///
|
||||||
|
/// assert!(date.at_most("2020-01-01"));
|
||||||
|
/// assert!(date.at_most("2020-01-02"));
|
||||||
|
/// assert!(date.at_most("2024-08-18"));
|
||||||
|
///
|
||||||
|
/// assert!(!date.at_most("2019-12-31"));
|
||||||
|
/// assert!(!date.at_most("2014-04-31"));
|
||||||
|
/// ```
|
||||||
|
pub fn at_most(&self, date: &str) -> bool {
|
||||||
|
Date::parse(date)
|
||||||
|
.map(|date| self <= &date)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if `self` occurs exactly on `date`.
|
||||||
|
///
|
||||||
|
/// If `date` is not exactly `self`, or if `date` is not in `%Y-%m-%d`
|
||||||
|
/// format, returns `false`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Date;
|
||||||
|
///
|
||||||
|
/// let date = Date::parse("2020-01-01").unwrap();
|
||||||
|
///
|
||||||
|
/// assert!(date.exactly("2020-01-01"));
|
||||||
|
///
|
||||||
|
/// assert!(!date.exactly("2019-12-31"));
|
||||||
|
/// assert!(!date.exactly("2014-04-31"));
|
||||||
|
/// assert!(!date.exactly("2020-01-02"));
|
||||||
|
/// assert!(!date.exactly("2024-08-18"));
|
||||||
|
/// ```
|
||||||
|
pub fn exactly(&self, date: &str) -> bool {
|
||||||
|
Date::parse(date)
|
||||||
|
.map(|date| self == &date)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Date {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let (y, m, d) = self.to_ymd();
|
||||||
|
write!(f, "{}-{}-{}", y, m, d)
|
||||||
|
}
|
||||||
|
}
|
357
src/lib.rs
357
src/lib.rs
@ -4,96 +4,96 @@
|
|||||||
//! `RUSTC` environment variable. If it is not set, then `rustc` is used. If
|
//! `RUSTC` environment variable. If it is not set, then `rustc` is used. If
|
||||||
//! that fails, no determination is made, and calls return `None`.
|
//! that fails, no determination is made, and calls return `None`.
|
||||||
//!
|
//!
|
||||||
//! # Example
|
//! # Examples
|
||||||
//!
|
//!
|
||||||
//! Check that the running compiler is a nightly release:
|
//! Set a `cfg` flag in `build.rs` if the running compiler was determined to be
|
||||||
|
//! at least version `1.13.0`:
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! extern crate version_check;
|
//! extern crate version_check as rustc;
|
||||||
//!
|
//!
|
||||||
//! match version_check::is_nightly() {
|
//! if rustc::is_min_version("1.13.0").unwrap_or(false) {
|
||||||
//! Some(true) => "running a nightly",
|
//! println!("cargo:rustc-cfg=question_mark_operator");
|
||||||
//! Some(false) => "not nightly",
|
//! }
|
||||||
//! None => "couldn't figure it out"
|
//! ```
|
||||||
|
//!
|
||||||
|
//! See [`is_max_version`] or [`is_exact_version`] to check if the compiler
|
||||||
|
//! is _at most_ or _exactly_ a certain version.
|
||||||
|
//!
|
||||||
|
//! Check that the running compiler was released on or after `2018-12-18`:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! extern crate version_check as rustc;
|
||||||
|
//!
|
||||||
|
//! match rustc::is_min_date("2018-12-18") {
|
||||||
|
//! Some(true) => "Yep! It's recent!",
|
||||||
|
//! Some(false) => "No, it's older.",
|
||||||
|
//! None => "Couldn't determine the rustc version."
|
||||||
//! };
|
//! };
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Check that the running compiler is at least version `1.13.0`:
|
//! See [`is_max_date`] or [`is_exact_date`] to check if the compiler was
|
||||||
|
//! released _prior to_ or _exactly on_ a certain date.
|
||||||
|
//!
|
||||||
|
//! Check that the running compiler supports feature flags:
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! extern crate version_check;
|
//! extern crate version_check as rustc;
|
||||||
//!
|
//!
|
||||||
//! match version_check::is_min_version("1.13.0") {
|
//! match rustc::is_feature_flaggable() {
|
||||||
//! Some((true, version)) => format!("Yes! It's: {}", version),
|
//! Some(true) => "Yes! It's a dev or nightly release!",
|
||||||
//! Some((false, version)) => format!("No! {} is too old!", version),
|
//! Some(false) => "No, it's stable or beta.",
|
||||||
//! None => "couldn't figure it out".into()
|
//! None => "Couldn't determine the rustc version."
|
||||||
//! };
|
//! };
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Check that the running compiler was released on or after `2016-12-18`:
|
//! Check that the running compiler is on the stable channel:
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! extern crate version_check;
|
//! extern crate version_check as rustc;
|
||||||
//!
|
//!
|
||||||
//! match version_check::is_min_date("2016-12-18") {
|
//! match rustc::Channel::read() {
|
||||||
//! Some((true, date)) => format!("Yes! It's: {}", date),
|
//! Some(c) if c.is_stable() => format!("Yes! It's stable."),
|
||||||
//! Some((false, date)) => format!("No! {} is too long ago!", date),
|
//! Some(c) => format!("No, the channel {} is not stable.", c),
|
||||||
//! None => "couldn't figure it out".into()
|
//! None => format!("Couldn't determine the rustc version.")
|
||||||
//! };
|
//! };
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! To interact with the version, release date, and release channel as structs,
|
||||||
|
//! use [`Version`], [`Date`], and [`Channel`], respectively. The [`triple()`]
|
||||||
|
//! function returns all three values efficiently.
|
||||||
|
//!
|
||||||
//! # Alternatives
|
//! # Alternatives
|
||||||
//!
|
//!
|
||||||
//! This crate is dead simple with no dependencies. If you need something more
|
//! This crate is dead simple with no dependencies. If you need something more
|
||||||
//! and don't care about panicking if the version cannot be obtained or adding
|
//! and don't care about panicking if the version cannot be obtained, or if you
|
||||||
//! dependencies, see [rustc_version](https://crates.io/crates/rustc_version).
|
//! don't mind adding dependencies, see
|
||||||
|
//! [rustc_version](https://crates.io/crates/rustc_version).
|
||||||
|
|
||||||
|
#![allow(deprecated)]
|
||||||
|
|
||||||
|
mod version;
|
||||||
|
mod channel;
|
||||||
|
mod date;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
// Convert a string of %Y-%m-%d to a single u32 maintaining ordering.
|
#[doc(inline)] pub use version::*;
|
||||||
fn str_to_ymd(ymd: &str) -> Option<u32> {
|
#[doc(inline)] pub use channel::*;
|
||||||
let ymd: Vec<u32> = ymd.split("-").filter_map(|s| s.parse::<u32>().ok()).collect();
|
#[doc(inline)] pub use date::*;
|
||||||
if ymd.len() != 3 {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
let (y, m, d) = (ymd[0], ymd[1], ymd[2]);
|
/// Parses (version, date) as available from rustc version string.
|
||||||
Some((y << 9) | (m << 5) | d)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert a string with prefix major-minor-patch to a single u64 maintaining
|
|
||||||
// ordering. Assumes none of the components are > 1048576.
|
|
||||||
fn str_to_mmp(mmp: &str) -> Option<u64> {
|
|
||||||
let mut mmp: Vec<u16> = mmp.split('-')
|
|
||||||
.nth(0)
|
|
||||||
.unwrap_or("")
|
|
||||||
.split('.')
|
|
||||||
.filter_map(|s| s.parse::<u16>().ok())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if mmp.is_empty() {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
while mmp.len() < 3 {
|
|
||||||
mmp.push(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let (maj, min, patch) = (mmp[0] as u64, mmp[1] as u64, mmp[2] as u64);
|
|
||||||
Some((maj << 32) | (min << 16) | patch)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns (version, date) as available.
|
|
||||||
fn version_and_date_from_rustc_version(s: &str) -> (Option<String>, Option<String>) {
|
fn version_and_date_from_rustc_version(s: &str) -> (Option<String>, Option<String>) {
|
||||||
let last_line = s.lines().last().unwrap_or(s);
|
let last_line = s.lines().last().unwrap_or(s);
|
||||||
let mut components = last_line.trim().split(" ");
|
let mut components = last_line.trim().split(" ");
|
||||||
let version = components.nth(1);
|
let version = components.nth(1);
|
||||||
let date = components.nth(1).map(|s| s.trim_right().trim_right_matches(")"));
|
let date = components.filter(|c| c.ends_with(')')).next()
|
||||||
|
.map(|s| s.trim_right().trim_right_matches(")").trim_left().trim_left_matches('('));
|
||||||
(version.map(|s| s.to_string()), date.map(|s| s.to_string()))
|
(version.map(|s| s.to_string()), date.map(|s| s.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns (version, date) as available.
|
/// Returns (version, date) as available from `rustc --version`.
|
||||||
fn get_version_and_date() -> Option<(Option<String>, Option<String>)> {
|
fn get_version_and_date() -> Option<(Option<String>, Option<String>)> {
|
||||||
env::var("RUSTC").ok()
|
env::var("RUSTC").ok()
|
||||||
.and_then(|rustc| Command::new(rustc).arg("--version").output().ok())
|
.and_then(|rustc| Command::new(rustc).arg("--version").output().ok())
|
||||||
@ -102,154 +102,187 @@ fn get_version_and_date() -> Option<(Option<String>, Option<String>)> {
|
|||||||
.map(|s| version_and_date_from_rustc_version(&s))
|
.map(|s| version_and_date_from_rustc_version(&s))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that the running or installed `rustc` was released no earlier than
|
/// Reads the triple of [`Version`], [`Channel`], and [`Date`] of the installed
|
||||||
|
/// or running `rustc`.
|
||||||
|
///
|
||||||
|
/// If any attribute cannot be determined (see the [top-level
|
||||||
|
/// documentation](crate)), returns `None`.
|
||||||
|
///
|
||||||
|
/// To obtain only one of three attributes, use [`Version::read()`],
|
||||||
|
/// [`Channel::read()`], or [`Date::read()`].
|
||||||
|
pub fn triple() -> Option<(Version, Channel, Date)> {
|
||||||
|
let (version_str, date_str) = match get_version_and_date() {
|
||||||
|
Some((Some(version), Some(date))) => (version, date),
|
||||||
|
_ => return None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Can't use `?` or `try!` for `Option` in 1.0.0.
|
||||||
|
match Version::parse(&version_str) {
|
||||||
|
Some(version) => match Channel::parse(&version_str) {
|
||||||
|
Some(channel) => match Date::parse(&date_str) {
|
||||||
|
Some(date) => Some((version, channel, date)),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks that the running or installed `rustc` was released **on or after**
|
||||||
/// some date.
|
/// some date.
|
||||||
///
|
///
|
||||||
/// The format of `min_date` must be YYYY-MM-DD. For instance: `2016-12-20` or
|
/// The format of `min_date` must be YYYY-MM-DD. For instance: `2016-12-20` or
|
||||||
/// `2017-01-09`.
|
/// `2017-01-09`.
|
||||||
///
|
///
|
||||||
/// If the date cannot be retrieved or parsed, or if `min_date` could not be
|
/// If the date cannot be retrieved or parsed, or if `min_date` could not be
|
||||||
/// parsed, returns `None`. Otherwise returns a tuple where the first value is
|
/// parsed, returns `None`. Otherwise returns `true` if the installed `rustc`
|
||||||
/// `true` if the installed `rustc` is at least from `min_date` and the second
|
/// was release on or after `min_date` and `false` otherwise.
|
||||||
/// value is the date (in YYYY-MM-DD) of the installed `rustc`.
|
pub fn is_min_date(min_date: &str) -> Option<bool> {
|
||||||
pub fn is_min_date(min_date: &str) -> Option<(bool, String)> {
|
match (Date::read(), Date::parse(min_date)) {
|
||||||
if let Some((_, Some(actual_date_str))) = get_version_and_date() {
|
(Some(rustc_date), Some(min_date)) => Some(rustc_date >= min_date),
|
||||||
str_to_ymd(&actual_date_str)
|
_ => None
|
||||||
.and_then(|actual| str_to_ymd(min_date).map(|min| (min, actual)))
|
|
||||||
.map(|(min, actual)| (actual >= min, actual_date_str))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that the running or installed `rustc` is at least some minimum
|
/// Checks that the running or installed `rustc` was released **on or before**
|
||||||
|
/// some date.
|
||||||
|
///
|
||||||
|
/// The format of `max_date` must be YYYY-MM-DD. For instance: `2016-12-20` or
|
||||||
|
/// `2017-01-09`.
|
||||||
|
///
|
||||||
|
/// If the date cannot be retrieved or parsed, or if `max_date` could not be
|
||||||
|
/// parsed, returns `None`. Otherwise returns `true` if the installed `rustc`
|
||||||
|
/// was release on or before `max_date` and `false` otherwise.
|
||||||
|
pub fn is_max_date(max_date: &str) -> Option<bool> {
|
||||||
|
match (Date::read(), Date::parse(max_date)) {
|
||||||
|
(Some(rustc_date), Some(max_date)) => Some(rustc_date <= max_date),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks that the running or installed `rustc` was released **exactly** on
|
||||||
|
/// some date.
|
||||||
|
///
|
||||||
|
/// The format of `date` must be YYYY-MM-DD. For instance: `2016-12-20` or
|
||||||
|
/// `2017-01-09`.
|
||||||
|
///
|
||||||
|
/// If the date cannot be retrieved or parsed, or if `date` could not be parsed,
|
||||||
|
/// returns `None`. Otherwise returns `true` if the installed `rustc` was
|
||||||
|
/// release on `date` and `false` otherwise.
|
||||||
|
pub fn is_exact_date(date: &str) -> Option<bool> {
|
||||||
|
match (Date::read(), Date::parse(date)) {
|
||||||
|
(Some(rustc_date), Some(date)) => Some(rustc_date == date),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks that the running or installed `rustc` is **at least** some minimum
|
||||||
/// version.
|
/// version.
|
||||||
///
|
///
|
||||||
/// The format of `min_version` is a semantic version: `1.3.0`, `1.15.0-beta`,
|
/// The format of `min_version` is a semantic version: `1.3.0`, `1.15.0-beta`,
|
||||||
/// `1.14.0`, `1.16.0-nightly`, etc.
|
/// `1.14.0`, `1.16.0-nightly`, etc.
|
||||||
///
|
///
|
||||||
/// If the version cannot be retrieved or parsed, or if `min_version` could not
|
/// If the version cannot be retrieved or parsed, or if `min_version` could not
|
||||||
/// be parsed, returns `None`. Otherwise returns a tuple where the first value
|
/// be parsed, returns `None`. Otherwise returns `true` if the installed `rustc`
|
||||||
/// is `true` if the installed `rustc` is at least `min_version` and the second
|
/// is at least `min_version` and `false` otherwise.
|
||||||
/// value is the version (semantic) of the installed `rustc`.
|
pub fn is_min_version(min_version: &str) -> Option<bool> {
|
||||||
pub fn is_min_version(min_version: &str) -> Option<(bool, String)> {
|
match (Version::read(), Version::parse(min_version)) {
|
||||||
if let Some((Some(actual_version_str), _)) = get_version_and_date() {
|
(Some(rustc_ver), Some(min_ver)) => Some(rustc_ver >= min_ver),
|
||||||
str_to_mmp(&actual_version_str)
|
_ => None
|
||||||
.and_then(|actual| str_to_mmp(min_version).map(|min| (min, actual)))
|
|
||||||
.map(|(min, actual)| (actual >= min, actual_version_str))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn version_channel_is(channel: &str) -> Option<bool> {
|
/// Checks that the running or installed `rustc` is **at most** some maximum
|
||||||
get_version_and_date()
|
/// version.
|
||||||
.and_then(|(version_str_opt, _)| version_str_opt)
|
|
||||||
.map(|version_str| version_str.contains(channel))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determines whether the running or installed `rustc` is on the nightly
|
|
||||||
/// channel.
|
|
||||||
///
|
///
|
||||||
/// If the version could not be determined, returns `None`. Otherwise returns
|
/// The format of `max_version` is a semantic version: `1.3.0`, `1.15.0-beta`,
|
||||||
/// `Some(true)` if the running version is a nightly release, and `Some(false)`
|
/// `1.14.0`, `1.16.0-nightly`, etc.
|
||||||
/// otherwise.
|
|
||||||
pub fn is_nightly() -> Option<bool> {
|
|
||||||
version_channel_is("nightly")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determines whether the running or installed `rustc` is on the beta channel.
|
|
||||||
///
|
///
|
||||||
/// If the version could not be determined, returns `None`. Otherwise returns
|
/// If the version cannot be retrieved or parsed, or if `max_version` could not
|
||||||
/// `Some(true)` if the running version is a beta release, and `Some(false)`
|
/// be parsed, returns `None`. Otherwise returns `true` if the installed `rustc`
|
||||||
/// otherwise.
|
/// is at most `max_version` and `false` otherwise.
|
||||||
pub fn is_beta() -> Option<bool> {
|
pub fn is_max_version(max_version: &str) -> Option<bool> {
|
||||||
version_channel_is("beta")
|
match (Version::read(), Version::parse(max_version)) {
|
||||||
|
(Some(rustc_ver), Some(max_ver)) => Some(rustc_ver <= max_ver),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether the running or installed `rustc` is on the dev channel.
|
/// Checks that the running or installed `rustc` is **exactly** some version.
|
||||||
///
|
///
|
||||||
/// If the version could not be determined, returns `None`. Otherwise returns
|
/// The format of `version` is a semantic version: `1.3.0`, `1.15.0-beta`,
|
||||||
/// `Some(true)` if the running version is a dev release, and `Some(false)`
|
/// `1.14.0`, `1.16.0-nightly`, etc.
|
||||||
/// otherwise.
|
///
|
||||||
pub fn is_dev() -> Option<bool> {
|
/// If the version cannot be retrieved or parsed, or if `version` could not be
|
||||||
version_channel_is("dev")
|
/// parsed, returns `None`. Otherwise returns `true` if the installed `rustc` is
|
||||||
|
/// exactly `version` and `false` otherwise.
|
||||||
|
pub fn is_exact_version(version: &str) -> Option<bool> {
|
||||||
|
match (Version::read(), Version::parse(version)) {
|
||||||
|
(Some(rustc_ver), Some(version)) => Some(rustc_ver == version),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether the running or installed `rustc` supports feature flags.
|
/// Checks whether the running or installed `rustc` supports feature flags.
|
||||||
|
///
|
||||||
/// In other words, if the channel is either "nightly" or "dev".
|
/// In other words, if the channel is either "nightly" or "dev".
|
||||||
///
|
///
|
||||||
/// If the version could not be determined, returns `None`. Otherwise returns
|
/// If the version could not be determined, returns `None`. Otherwise returns
|
||||||
/// `Some(true)` if the running version supports features, and `Some(false)`
|
/// `true` if the running version supports feature flags and `false` otherwise.
|
||||||
/// otherwise.
|
pub fn is_feature_flaggable() -> Option<bool> {
|
||||||
pub fn supports_features() -> Option<bool> {
|
Channel::read().map(|c| c.supports_features())
|
||||||
match is_nightly() {
|
|
||||||
b@Some(true) => b,
|
|
||||||
_ => is_dev()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::version_and_date_from_rustc_version;
|
use super::version_and_date_from_rustc_version;
|
||||||
use super::str_to_mmp;
|
|
||||||
|
|
||||||
macro_rules! check_mmp {
|
macro_rules! check_parse {
|
||||||
($string:expr => ($x:expr, $y:expr, $z:expr)) => (
|
($s:expr => $v:expr, $d:expr) => (
|
||||||
if let Some(mmp) = str_to_mmp($string) {
|
if let (Some(v), d) = version_and_date_from_rustc_version($s) {
|
||||||
let expected = $x << 32 | $y << 16 | $z;
|
let e_d: Option<&str> = $d.into();
|
||||||
if mmp != expected {
|
assert_eq!((v, d), ($v.into(), e_d.map(|s| s.into())));
|
||||||
panic!("{:?} didn't parse as {}.{}.{}.", $string, $x, $y, $z);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic!("{:?} didn't parse for mmp testing.", $string);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! check_version {
|
|
||||||
($s:expr => ($x:expr, $y:expr, $z:expr)) => (
|
|
||||||
if let (Some(version_str), _) = version_and_date_from_rustc_version($s) {
|
|
||||||
check_mmp!(&version_str => ($x, $y, $z));
|
|
||||||
} else {
|
} else {
|
||||||
panic!("{:?} didn't parse for version testing.", $s);
|
panic!("{:?} didn't parse for version testing.", $s);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_str_to_mmp() {
|
|
||||||
check_mmp!("1.18.0" => (1, 18, 0));
|
|
||||||
check_mmp!("1.19.0" => (1, 19, 0));
|
|
||||||
check_mmp!("1.19.0-nightly" => (1, 19, 0));
|
|
||||||
check_mmp!("1.12.2349" => (1, 12, 2349));
|
|
||||||
check_mmp!("0.12" => (0, 12, 0));
|
|
||||||
check_mmp!("1.12.5" => (1, 12, 5));
|
|
||||||
check_mmp!("1.12" => (1, 12, 0));
|
|
||||||
check_mmp!("1" => (1, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_version_parse() {
|
fn test_version_parse() {
|
||||||
check_version!("rustc 1.18.0" => (1, 18, 0));
|
check_parse!("rustc 1.18.0" => "1.18.0", None);
|
||||||
check_version!("rustc 1.8.0" => (1, 8, 0));
|
check_parse!("rustc 1.8.0" => "1.8.0", None);
|
||||||
check_version!("rustc 1.20.0-nightly" => (1, 20, 0));
|
check_parse!("rustc 1.20.0-nightly" => "1.20.0-nightly", None);
|
||||||
check_version!("rustc 1.20" => (1, 20, 0));
|
check_parse!("rustc 1.20" => "1.20", None);
|
||||||
check_version!("rustc 1.3" => (1, 3, 0));
|
check_parse!("rustc 1.3" => "1.3", None);
|
||||||
check_version!("rustc 1" => (1, 0, 0));
|
check_parse!("rustc 1" => "1", None);
|
||||||
check_version!("rustc 1.2.5.6" => (1, 2, 5));
|
check_parse!("rustc 1.5.1-beta" => "1.5.1-beta", None);
|
||||||
check_version!("rustc 1.5.1-beta" => (1, 5, 1));
|
|
||||||
check_version!("rustc 1.20.0-nightly (d84693b93 2017-07-09)" => (1, 20, 0));
|
|
||||||
check_version!("rustc 1.20.0 (d84693b93 2017-07-09)" => (1, 20, 0));
|
|
||||||
check_version!("rustc 1.20.0 (2017-07-09)" => (1, 20, 0));
|
|
||||||
check_version!("rustc 1.20.0-dev (2017-07-09)" => (1, 20, 0));
|
|
||||||
|
|
||||||
check_version!("warning: invalid logging spec 'warning', ignoring it
|
// Because of 1.0.0, we can't use Option<T>: From<T>.
|
||||||
rustc 1.30.0-nightly (3bc2ca7e4 2018-09-20)" => (1, 30, 0));
|
check_parse!("rustc 1.20.0 (2017-07-09)"
|
||||||
check_version!("warning: invalid logging spec 'warning', ignoring it\n
|
=> "1.20.0", Some("2017-07-09"));
|
||||||
rustc 1.30.0-nightly (3bc2ca7e4 2018-09-20)" => (1, 30, 0));
|
|
||||||
check_version!("warning: invalid logging spec 'warning', ignoring it
|
check_parse!("rustc 1.20.0-dev (2017-07-09)"
|
||||||
warning: something else went wrong
|
=> "1.20.0-dev", Some("2017-07-09"));
|
||||||
rustc 1.30.0-nightly (3bc2ca7e4 2018-09-20)" => (1, 30, 0));
|
|
||||||
|
check_parse!("rustc 1.20.0-nightly (d84693b93 2017-07-09)"
|
||||||
|
=> "1.20.0-nightly", Some("2017-07-09"));
|
||||||
|
|
||||||
|
check_parse!("rustc 1.20.0 (d84693b93 2017-07-09)"
|
||||||
|
=> "1.20.0", Some("2017-07-09"));
|
||||||
|
|
||||||
|
check_parse!("warning: invalid logging spec 'warning', ignoring it
|
||||||
|
rustc 1.30.0-nightly (3bc2ca7e4 2018-09-20)"
|
||||||
|
=> "1.30.0-nightly", Some("2018-09-20"));
|
||||||
|
|
||||||
|
check_parse!("warning: invalid logging spec 'warning', ignoring it\n
|
||||||
|
rustc 1.30.0-nightly (3bc2ca7e4 2018-09-20)"
|
||||||
|
=> "1.30.0-nightly", Some("2018-09-20"));
|
||||||
|
|
||||||
|
check_parse!("warning: invalid logging spec 'warning', ignoring it
|
||||||
|
warning: something else went wrong
|
||||||
|
rustc 1.30.0-nightly (3bc2ca7e4 2018-09-20)"
|
||||||
|
=> "1.30.0-nightly", Some("2018-09-20"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
221
src/version.rs
Normal file
221
src/version.rs
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// Version number: `major.minor.patch`, ignoring release channel.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
|
||||||
|
pub struct Version(u64);
|
||||||
|
|
||||||
|
impl Version {
|
||||||
|
fn to_mmp(&self) -> (u16, u16, u16) {
|
||||||
|
let major = self.0 >> 32;
|
||||||
|
let minor = (self.0 << 32) >> 48;
|
||||||
|
let patch = (self.0 << 48) >> 48;
|
||||||
|
(major as u16, minor as u16, patch as u16)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the version of the running compiler. If it cannot be determined
|
||||||
|
/// (see the [top-level documentation](crate)), returns `None`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Version;
|
||||||
|
///
|
||||||
|
/// match Version::read() {
|
||||||
|
/// Some(d) => format!("Version is: {}", d),
|
||||||
|
/// None => format!("Failed to read the version.")
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
pub fn read() -> Option<Version> {
|
||||||
|
::get_version_and_date()
|
||||||
|
.and_then(|(version, _)| version)
|
||||||
|
.and_then(|version| Version::parse(&version))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Parse a Rust release version (of the form
|
||||||
|
/// `major[.minor[.patch[-channel]]]`), ignoring the release channel, if
|
||||||
|
/// any. Returns `None` if `version` is not a valid Rust version string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Version;
|
||||||
|
///
|
||||||
|
/// let version = Version::parse("1.18.0").unwrap();
|
||||||
|
/// assert!(version.exactly("1.18.0"));
|
||||||
|
///
|
||||||
|
/// let version = Version::parse("1.20.0-nightly").unwrap();
|
||||||
|
/// assert!(version.exactly("1.20.0"));
|
||||||
|
/// assert!(version.exactly("1.20.0-beta"));
|
||||||
|
///
|
||||||
|
/// let version = Version::parse("1.3").unwrap();
|
||||||
|
/// assert!(version.exactly("1.3.0"));
|
||||||
|
///
|
||||||
|
/// let version = Version::parse("1").unwrap();
|
||||||
|
/// assert!(version.exactly("1.0.0"));
|
||||||
|
///
|
||||||
|
/// assert!(Version::parse("one.two.three").is_none());
|
||||||
|
/// ```
|
||||||
|
pub fn parse(version: &str) -> Option<Version> {
|
||||||
|
let mut mmp: Vec<u16> = version.split('-')
|
||||||
|
.nth(0)
|
||||||
|
.unwrap_or("")
|
||||||
|
.split('.')
|
||||||
|
.filter_map(|s| s.parse::<u16>().ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if mmp.is_empty() {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
while mmp.len() < 3 {
|
||||||
|
mmp.push(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (maj, min, patch) = (mmp[0] as u64, mmp[1] as u64, mmp[2] as u64);
|
||||||
|
Some(Version((maj << 32) | (min << 16) | patch))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if `self` is greater than or equal to `version`.
|
||||||
|
///
|
||||||
|
/// If `version` is greater than `self`, or if `version` is not a valid Rust
|
||||||
|
/// version string, returns `false`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Version;
|
||||||
|
///
|
||||||
|
/// let version = Version::parse("1.35.0").unwrap();
|
||||||
|
///
|
||||||
|
/// assert!(version.at_least("1.33.0"));
|
||||||
|
/// assert!(version.at_least("1.35.0"));
|
||||||
|
/// assert!(version.at_least("1.13.2"));
|
||||||
|
///
|
||||||
|
/// assert!(!version.at_least("1.35.1"));
|
||||||
|
/// assert!(!version.at_least("1.55.0"));
|
||||||
|
/// ```
|
||||||
|
pub fn at_least(&self, version: &str) -> bool {
|
||||||
|
Version::parse(version)
|
||||||
|
.map(|version| self >= &version)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if `self` is less than or equal to `version`.
|
||||||
|
///
|
||||||
|
/// If `version` is less than `self`, or if `version` is not a valid Rust
|
||||||
|
/// version string, returns `false`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Version;
|
||||||
|
///
|
||||||
|
/// let version = Version::parse("1.35.0").unwrap();
|
||||||
|
///
|
||||||
|
/// assert!(version.at_most("1.35.1"));
|
||||||
|
/// assert!(version.at_most("1.55.0"));
|
||||||
|
/// assert!(version.at_most("1.35.0"));
|
||||||
|
///
|
||||||
|
/// assert!(!version.at_most("1.33.0"));
|
||||||
|
/// assert!(!version.at_most("1.13.2"));
|
||||||
|
/// ```
|
||||||
|
pub fn at_most(&self, version: &str) -> bool {
|
||||||
|
Version::parse(version)
|
||||||
|
.map(|version| self <= &version)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if `self` is exactly equal to `version`.
|
||||||
|
///
|
||||||
|
/// If `version` is not equal to `self`, or if `version` is not a valid Rust
|
||||||
|
/// version string, returns `false`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use version_check::Version;
|
||||||
|
///
|
||||||
|
/// let version = Version::parse("1.35.0").unwrap();
|
||||||
|
///
|
||||||
|
/// assert!(version.exactly("1.35.0"));
|
||||||
|
///
|
||||||
|
/// assert!(!version.exactly("1.33.0"));
|
||||||
|
/// assert!(!version.exactly("1.35.1"));
|
||||||
|
/// assert!(!version.exactly("1.13.2"));
|
||||||
|
/// ```
|
||||||
|
pub fn exactly(&self, version: &str) -> bool {
|
||||||
|
Version::parse(version)
|
||||||
|
.map(|version| self == &version)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Version {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let (major, minor, patch) = self.to_mmp();
|
||||||
|
write!(f, "{}.{}.{}", major, minor, patch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::Version;
|
||||||
|
|
||||||
|
macro_rules! check_mmp {
|
||||||
|
($s:expr => ($x:expr, $y:expr, $z:expr)) => (
|
||||||
|
if let Some(v) = Version::parse($s) {
|
||||||
|
if v.to_mmp() != ($x, $y, $z) {
|
||||||
|
panic!("{:?} ({}) didn't parse as {}.{}.{}.", $s, v, $x, $y, $z);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("{:?} didn't parse for mmp testing.", $s);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_str_to_mmp() {
|
||||||
|
check_mmp!("1.18.0" => (1, 18, 0));
|
||||||
|
check_mmp!("3.19.0" => (3, 19, 0));
|
||||||
|
check_mmp!("1.19.0-nightly" => (1, 19, 0));
|
||||||
|
check_mmp!("1.12.2349" => (1, 12, 2349));
|
||||||
|
check_mmp!("0.12" => (0, 12, 0));
|
||||||
|
check_mmp!("1.12.5" => (1, 12, 5));
|
||||||
|
check_mmp!("1.12" => (1, 12, 0));
|
||||||
|
check_mmp!("1" => (1, 0, 0));
|
||||||
|
check_mmp!("1.4.4-nightly (d84693b93 2017-07-09)" => (1, 4, 4));
|
||||||
|
check_mmp!("1.58879.4478-dev" => (1, 58879, 4478));
|
||||||
|
check_mmp!("1.58879.4478-dev (d84693b93 2017-07-09)" => (1, 58879, 4478));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_comparisons() {
|
||||||
|
let version = Version::parse("1.18.0").unwrap();
|
||||||
|
assert!(version.exactly("1.18.0"));
|
||||||
|
assert!(version.at_least("1.12.0"));
|
||||||
|
assert!(version.at_most("1.18.1"));
|
||||||
|
assert!(!version.exactly("1.19.0"));
|
||||||
|
assert!(!version.exactly("1.18.1"));
|
||||||
|
|
||||||
|
let version = Version::parse("1.20.0-nightly").unwrap();
|
||||||
|
assert!(version.exactly("1.20.0-beta"));
|
||||||
|
assert!(version.exactly("1.20.0-nightly"));
|
||||||
|
assert!(version.exactly("1.20.0"));
|
||||||
|
assert!(!version.exactly("1.19"));
|
||||||
|
|
||||||
|
let version = Version::parse("1.3").unwrap();
|
||||||
|
assert!(version.exactly("1.3.0"));
|
||||||
|
assert!(version.exactly("1.3.0-stable"));
|
||||||
|
assert!(version.exactly("1.3"));
|
||||||
|
assert!(!version.exactly("1.5.0-stable"));
|
||||||
|
|
||||||
|
let version = Version::parse("1").unwrap();
|
||||||
|
assert!(version.exactly("1.0.0"));
|
||||||
|
assert!(version.exactly("1.0"));
|
||||||
|
assert!(version.exactly("1"));
|
||||||
|
|
||||||
|
assert!(Version::parse("one.two.three").is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user