referrerpolicy=no-referrer-when-downgrade

substrate_wasm_builder/
version.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use std::cmp::Ordering;
19
20/// The version of rustc/cargo.
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub struct Version {
23	pub major: u32,
24	pub minor: u32,
25	pub patch: u32,
26	pub is_nightly: bool,
27	pub year: Option<u32>,
28	pub month: Option<u32>,
29	pub day: Option<u32>,
30}
31
32impl Version {
33	/// Returns if `self` is a stable version.
34	pub fn is_stable(&self) -> bool {
35		!self.is_nightly
36	}
37
38	/// Return if `self` is a nightly version.
39	pub fn is_nightly(&self) -> bool {
40		self.is_nightly
41	}
42
43	/// Extract from the given `version` string.
44	pub fn extract(version: &str) -> Option<Self> {
45		let mut is_nightly = false;
46		let version_parts = version
47			.trim()
48			.split(" ")
49			.nth(1)?
50			.split(".")
51			.filter_map(|v| {
52				if let Some(rest) = v.strip_suffix("-nightly") {
53					is_nightly = true;
54					rest.parse().ok()
55				} else {
56					v.parse().ok()
57				}
58			})
59			.collect::<Vec<u32>>();
60
61		if version_parts.len() != 3 {
62			return None
63		}
64
65		let date_parts = version
66			.split(" ")
67			.nth(3)
68			.map(|date| {
69				date.split("-")
70					.filter_map(|v| v.trim().strip_suffix(")").unwrap_or(v).parse().ok())
71					.collect::<Vec<u32>>()
72			})
73			.unwrap_or_default();
74
75		Some(Version {
76			major: version_parts[0],
77			minor: version_parts[1],
78			patch: version_parts[2],
79			is_nightly,
80			year: date_parts.get(0).copied(),
81			month: date_parts.get(1).copied(),
82			day: date_parts.get(2).copied(),
83		})
84	}
85}
86
87/// Ordering is done in the following way:
88///
89/// 1. `stable` > `nightly`
90/// 2. Then compare major, minor and patch.
91/// 3. Last compare the date.
92impl Ord for Version {
93	fn cmp(&self, other: &Self) -> Ordering {
94		if self == other {
95			return Ordering::Equal
96		}
97
98		// Ensure that `stable > nightly`
99		if self.is_stable() && other.is_nightly() {
100			return Ordering::Greater
101		} else if self.is_nightly() && other.is_stable() {
102			return Ordering::Less
103		}
104
105		let to_compare = [
106			(Some(self.major), Some(other.major)),
107			(Some(self.minor), Some(other.minor)),
108			(Some(self.patch), Some(other.patch)),
109			(self.year, other.year),
110			(self.month, other.month),
111			(self.day, other.day),
112		];
113
114		to_compare
115			.iter()
116			.find_map(|(l, r)| if l != r { l.partial_cmp(&r) } else { None })
117			// We already checked this right at the beginning, so we should never return here
118			// `Equal`.
119			.unwrap_or(Ordering::Equal)
120	}
121}
122
123impl PartialOrd for Version {
124	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
125		Some(self.cmp(other))
126	}
127}
128
129#[cfg(test)]
130mod tests {
131	use super::*;
132
133	#[test]
134	fn version_compare_and_extract_works() {
135		let version_1_66_0 = Version::extract("cargo 1.66.0 (d65d197ad 2022-11-15)").unwrap();
136		let version_1_66_1 = Version::extract("cargo 1.66.1 (d65d197ad 2022-11-15)").unwrap();
137		let version_1_66_0_nightly =
138			Version::extract("cargo 1.66.0-nightly (d65d197ad 2022-10-15)").unwrap();
139		let version_1_66_0_nightly_older_date =
140			Version::extract("cargo 1.66.0-nightly (d65d197ad 2022-10-14)").unwrap();
141		let version_1_65_0 = Version::extract("cargo 1.65.0 (d65d197ad 2022-10-15)").unwrap();
142		let version_1_65_0_older_date =
143			Version::extract("cargo 1.65.0 (d65d197ad 2022-10-14)").unwrap();
144
145		assert!(version_1_66_1 > version_1_66_0);
146		assert!(version_1_66_1 > version_1_65_0);
147		assert!(version_1_66_1 > version_1_66_0_nightly);
148		assert!(version_1_66_1 > version_1_66_0_nightly_older_date);
149		assert!(version_1_66_1 > version_1_65_0_older_date);
150
151		assert!(version_1_66_0 > version_1_65_0);
152		assert!(version_1_66_0 > version_1_66_0_nightly);
153		assert!(version_1_66_0 > version_1_66_0_nightly_older_date);
154		assert!(version_1_66_0 > version_1_65_0_older_date);
155
156		assert!(version_1_65_0 > version_1_66_0_nightly);
157		assert!(version_1_65_0 > version_1_66_0_nightly_older_date);
158		assert!(version_1_65_0 > version_1_65_0_older_date);
159
160		let mut versions = vec![
161			version_1_66_0,
162			version_1_66_0_nightly,
163			version_1_66_0_nightly_older_date,
164			version_1_65_0_older_date,
165			version_1_65_0,
166			version_1_66_1,
167		];
168		versions.sort_by(|a, b| b.cmp(a));
169
170		let expected_versions_order = vec![
171			version_1_66_1,
172			version_1_66_0,
173			version_1_65_0,
174			version_1_65_0_older_date,
175			version_1_66_0_nightly,
176			version_1_66_0_nightly_older_date,
177		];
178		assert_eq!(expected_versions_order, versions);
179	}
180
181	#[test]
182	fn parse_with_newline() {
183		let version_1_66_0 = Version::extract("cargo 1.66.0 (d65d197ad 2022-11-15)\n").unwrap();
184		assert_eq!(
185			Version {
186				major: 1,
187				minor: 66,
188				patch: 0,
189				is_nightly: false,
190				year: Some(2022),
191				month: Some(11),
192				day: Some(15),
193			},
194			version_1_66_0,
195		);
196	}
197
198	#[test]
199	fn version_without_hash_and_date() {
200		// Apparently there are installations that print without the hash and date.
201		let version_1_69_0 = Version::extract("cargo 1.69.0-nightly").unwrap();
202		assert_eq!(
203			Version {
204				major: 1,
205				minor: 69,
206				patch: 0,
207				is_nightly: true,
208				year: None,
209				month: None,
210				day: None,
211			},
212			version_1_69_0,
213		);
214	}
215
216	#[test]
217	fn parse_rustc_version() {
218		let version = Version::extract("rustc 1.73.0 (cc66ad468 2023-10-03)").unwrap();
219		assert_eq!(
220			version,
221			Version {
222				major: 1,
223				minor: 73,
224				patch: 0,
225				is_nightly: false,
226				year: Some(2023),
227				month: Some(10),
228				day: Some(03),
229			}
230		);
231	}
232}