Skip to content

Commit fb2b878

Browse files
committedJan 15, 2017
Initial commit.
0 parents  commit fb2b878

File tree

3 files changed

+168
-0
lines changed

3 files changed

+168
-0
lines changed
 

‎.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
target
2+
Cargo.lock

‎Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "version-check"
3+
version = "0.1.0"
4+
authors = ["Sergio Benitez <sb@sergio.bz>"]
5+
6+
[dependencies]

‎src/lib.rs

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
//! This tiny crate checks that the running or installed `rustc` meets some
2+
//! version requirements. The version is queried by calling the Rust compiler
3+
//! with `--version`. The path to the compiler is determined first via the
4+
//! `RUSTC` environment variable. If it is not set, then `rustc` is used. If
5+
//! that fails, no determination is made, and calls return `None`.
6+
//!
7+
//! # Example
8+
//!
9+
//! Check that the running compiler is a nightly release:
10+
//!
11+
//! ```rust
12+
//! extern crate version_check;
13+
//!
14+
//! match version_check::is_nightly() {
15+
//! Some(true) => "running a nightly",
16+
//! Some(false) => "not nightly",
17+
//! None => "couldn't figure it out"
18+
//! };
19+
//! ```
20+
//!
21+
//! Check that the running compiler is at least version `1.13.0`:
22+
//!
23+
//! ```rust
24+
//! extern crate version_check;
25+
//!
26+
//! match version_check::is_min_version("1.13.0") {
27+
//! Some((true, version)) => format!("Yes! It's: {}", version),
28+
//! Some((false, version)) => format!("No! {} is too old!", version),
29+
//! None => "couldn't figure it out".into()
30+
//! };
31+
//! ```
32+
//!
33+
//! Check that the running compiler was released on or after `2016-12-18`:
34+
//!
35+
//! ```rust
36+
//! extern crate version_check;
37+
//!
38+
//! match version_check::is_min_date("2016-12-18") {
39+
//! Some((true, date)) => format!("Yes! It's: {}", date),
40+
//! Some((false, date)) => format!("No! {} is too long ago!", date),
41+
//! None => "couldn't figure it out".into()
42+
//! };
43+
//! ```
44+
//!
45+
//! # Alternatives
46+
//!
47+
//! This crate is dead simple with no dependencies. If you need something more
48+
//! and don't care about panicking if the version cannot be obtained or adding
49+
//! dependencies, see [rustc_version](https://crates.io/crates/rustc_version).
50+
51+
use std::env;
52+
use std::process::Command;
53+
54+
// Convert a string of %Y-%m-%d to a single u32 maintaining ordering.
55+
fn str_to_ymd(ymd: &str) -> Option<u32> {
56+
let ymd: Vec<u32> = ymd.split("-").filter_map(|s| s.parse::<u32>().ok()).collect();
57+
if ymd.len() != 3 {
58+
return None
59+
}
60+
61+
let (y, m, d) = (ymd[0], ymd[1], ymd[2]);
62+
Some((y << 9) | (m << 5) | d)
63+
}
64+
65+
// Convert a string with prefix major-minor-patch to a single u64 maintaining
66+
// ordering. Assumes none of the components are > 1048576.
67+
fn str_to_mmp(mmp: &str) -> Option<u64> {
68+
let mmp: Vec<u16> = mmp.split('-')
69+
.nth(0)
70+
.unwrap_or("")
71+
.split('.')
72+
.filter_map(|s| s.parse::<u16>().ok())
73+
.collect();
74+
75+
if mmp.len() != 3 {
76+
return None
77+
}
78+
79+
let (maj, min, patch) = (mmp[0] as u64, mmp[1] as u64, mmp[2] as u64);
80+
Some((maj << 32) | (min << 16) | patch)
81+
}
82+
83+
fn get_version_and_date() -> Option<(String, String)> {
84+
let output = env::var("RUSTC").ok()
85+
.and_then(|rustc| Command::new(rustc).arg("--version").output().ok())
86+
.or_else(|| Command::new("rustc").arg("--version").output().ok())
87+
.and_then(|output| String::from_utf8(output.stdout).ok())
88+
.map(|s| {
89+
let mut components = s.split(" ");
90+
let version = components.nth(1);
91+
let date = components.nth(1).map(|s| s.trim_right().trim_right_matches(")"));
92+
(version.map(|s| s.to_string()), date.map(|s| s.to_string()))
93+
});
94+
95+
match output {
96+
Some((Some(version), Some(date))) => Some((version, date)),
97+
_ => None
98+
}
99+
}
100+
101+
/// Checks that the running or installed `rustc` was released no earlier than
102+
/// some date.
103+
///
104+
/// The format of `min_date` must be YYYY-MM-DD. For instance: `2016-12-20` or
105+
/// `2017-01-09`.
106+
///
107+
/// If the date cannot be retrieved or parsed, or if `min_date` could not be
108+
/// parsed, returns `None`. Otherwise returns a tuple where the first value is
109+
/// `true` if the installed `rustc` is at least from `min_data` and the second
110+
/// value is the date (in YYYY-MM-DD) of the installed `rustc`.
111+
pub fn is_min_date(min_date: &str) -> Option<(bool, String)> {
112+
if let Some((_, actual_date_str)) = get_version_and_date() {
113+
str_to_ymd(&actual_date_str)
114+
.and_then(|actual| str_to_ymd(min_date).map(|min| (min, actual)))
115+
.map(|(min, actual)| (actual >= min, actual_date_str))
116+
} else {
117+
None
118+
}
119+
}
120+
121+
/// Checks that the running or installed `rustc` is at least some minimum
122+
/// version.
123+
///
124+
/// The format of `min_version` is a semantic version: `1.15.0-beta`, `1.14.0`,
125+
/// `1.16.0-nightly`, etc.
126+
///
127+
/// If the version cannot be retrieved or parsed, or if `min_version` could not
128+
/// be parsed, returns `None`. Otherwise returns a tuple where the first value
129+
/// is `true` if the installed `rustc` is at least `min_version` and the second
130+
/// value is the version (semantic) of the installed `rustc`.
131+
pub fn is_min_version(min_version: &str) -> Option<(bool, String)> {
132+
if let Some((actual_version_str, _)) = get_version_and_date() {
133+
str_to_mmp(&actual_version_str)
134+
.and_then(|actual| str_to_mmp(min_version).map(|min| (min, actual)))
135+
.map(|(min, actual)| (actual >= min, actual_version_str))
136+
} else {
137+
None
138+
}
139+
}
140+
141+
/// Determines whether the running or installed `rustc` is on the nightly
142+
/// channel.
143+
///
144+
/// If the version could not be determined, returns `None`. Otherwise returns
145+
/// `Some(true)` if the running version is a nightly release, and `Some(false)`
146+
/// otherwise.
147+
pub fn is_nightly() -> Option<bool> {
148+
get_version_and_date()
149+
.map(|(actual_version_str, _)| actual_version_str.contains("nightly"))
150+
}
151+
152+
/// Determines whether the running or installed `rustc` is on the beta channel.
153+
///
154+
/// If the version could not be determined, returns `None`. Otherwise returns
155+
/// `Some(true)` if the running version is a beta release, and `Some(false)`
156+
/// otherwise.
157+
pub fn is_beta() -> Option<bool> {
158+
get_version_and_date()
159+
.map(|(actual_version_str, _)| actual_version_str.contains("beta"))
160+
}

0 commit comments

Comments
 (0)
Please sign in to comment.