Skip to main content

rust_release/
lib.rs

1//! # rust-release
2//!
3//! The [`rust-release`] crate defines a set of types which model a Rust release.
4//!
5//! This project is part of the [`rust-releases`] and [`cargo-msrv`] projects.
6//!
7//! In case you have a feature request, question, bug, or have another reason to
8//! contact the developers, please create a new issue at the `rust-releases`
9//! [`repository`].
10//!
11//! [`rust-releases`]: https://github.com/foresterre/rust-releases
12//! [`cargo-msrv`]: https://github.com/foresterre/cargo-msrv
13//! [`repository`]: https://github.com/foresterre/rust-releases/issues
14#![warn(clippy::all)]
15#![deny(missing_docs)]
16#![deny(unsafe_code)]
17
18// exports
19pub use rust_toolchain::channel::{Beta, Nightly, Stable};
20use std::cmp;
21use std::fmt::Debug;
22
23/// A module for an unrefined Date type, solely used as a version number.
24///
25/// Do not use as your date type!
26pub mod date {
27    pub use rust_toolchain::Date;
28}
29/// Describes toolchains in so far they're relevant to a release
30pub mod toolchain {
31    pub use rust_toolchain::{Channel, Component, RustVersion, Target, Toolchain};
32}
33
34/// Describes the version of a release
35pub mod version;
36
37/// Type to model a Rust release.
38///
39/// # PartialEq, Eq, Ord, PartialOrd
40///
41/// With respect to the PartialEq, Eq, PartialOrd and Ord traits, a [`RustRelease`]
42/// `a` is equal, less, or greater than a [`RustRelease`] `b` iff respectively the
43/// `a.version` field is equal, less, or greater than `b.version`.
44#[derive(Clone, Debug)]
45pub struct RustRelease<V: Debug, C = ()> {
46    /// The version of a [`RustRelease`].
47    ///
48    /// The versioning scheme depends on the channel, which is why the version
49    /// type is a generic. In this library, the `V` is always substituted by one
50    /// of the following types: [`Stable`], [`Beta`] or [`Nightly`].
51    ///
52    /// [`Stable`] and [`Beta`] carry a semver version number, while [`Nightly`]
53    /// is versioned by a date.
54    pub version: V,
55    /// The release date of the release.
56    ///
57    /// The field is optional, because the value may be absent from a data source.
58    pub release_date: Option<date::Date>,
59    /// The toolchains associated with the release.
60    ///
61    /// The field may be empty if toolchains were absent from a data source.
62    pub toolchains: Vec<toolchain::Toolchain>,
63    /// Arbitrary extra data
64    pub context: C, // Eventually, I want to add this again which can be used to tag the release with arbitrary data
65}
66
67impl<V: PartialEq + Debug, C> PartialEq for RustRelease<V, C> {
68    fn eq(&self, other: &Self) -> bool {
69        self.version.eq(&other.version)
70    }
71}
72
73impl<V: Eq + Debug, C> Eq for RustRelease<V, C> {}
74
75impl<V: PartialOrd + Debug, C> PartialOrd for RustRelease<V, C> {
76    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
77        self.version.partial_cmp(&other.version)
78    }
79}
80
81impl<V: Ord + Debug, C> Ord for RustRelease<V, C> {
82    fn cmp(&self, other: &Self) -> cmp::Ordering {
83        self.version.cmp(&other.version)
84    }
85}
86
87impl<V: Debug> RustRelease<V, ()> {
88    /// Create a new RustRelease instance using a version, optionally
89    /// a release date, and an iterator of toolchains.
90    pub fn new(
91        version: V,
92        release_date: Option<rust_toolchain::Date>,
93        toolchains: impl IntoIterator<Item = toolchain::Toolchain>,
94    ) -> Self {
95        Self {
96            version,
97            release_date,
98            toolchains: toolchains.into_iter().collect(),
99            context: (),
100        }
101    }
102}
103
104impl<V: Debug, C> RustRelease<V, C> {
105    /// The version of a release.
106    ///
107    /// The 3 component MAJOR.MINOR.PATCH version number of the release
108    pub fn version(&self) -> &V {
109        &self.version
110    }
111
112    /// Release date of the Rust release, if known
113    pub fn release_date(&self) -> Option<&date::Date> {
114        self.release_date.as_ref()
115    }
116
117    /// Toolchains associated with the release
118    pub fn toolchains(&self) -> impl Iterator<Item = &toolchain::Toolchain> {
119        self.toolchains.iter()
120    }
121}
122
123/// A combination of a channel and the version number.
124///
125/// For stable and beta releases, we have a three component MAJOR.MINOR.PATCH
126/// version number. For nightly releases, we have a release date.
127#[derive(Clone, Debug, Eq, PartialEq)]
128pub enum ReleaseVersion {
129    /// A stable channel release version
130    Stable(Stable),
131    /// A beta channel release version
132    Beta(Beta),
133    /// A nightly channel release version
134    Nightly(Nightly),
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140    use crate::toolchain::Toolchain;
141    use rust_toolchain::RustVersion;
142    use std::collections::HashSet;
143
144    fn fake(stable: Stable, date: Option<rust_toolchain::Date>) -> Toolchain {
145        Toolchain::new(
146            rust_toolchain::Channel::Stable(stable),
147            date,
148            rust_toolchain::Target::host(),
149            HashSet::new(),
150            HashSet::new(),
151        )
152    }
153
154    #[test]
155    fn can_instantiate() {
156        let stable = Stable {
157            version: RustVersion::new(1, 82, 0),
158        };
159        let version = ReleaseVersion::Stable(stable.clone());
160        let release = RustRelease::new(version, None, vec![fake(stable.clone(), None)]);
161
162        assert_eq!(release.version(), &ReleaseVersion::Stable(stable));
163    }
164
165    #[yare::parameterized(
166        some = { Some(rust_toolchain::Date::new(2024, 1, 1)) },
167        none = { None },
168    )]
169    fn can_instantiate_deux(date: Option<rust_toolchain::Date>) {
170        let stable = Stable {
171            version: RustVersion::new(1, 82, 0),
172        };
173        let version = ReleaseVersion::Stable(stable.clone());
174        let release = RustRelease::new(version, date.clone(), vec![fake(stable, date)]);
175
176        let target_date = release.toolchains().next().unwrap().date();
177
178        assert_eq!(release.release_date(), target_date);
179    }
180}