1#![warn(clippy::all)]
15#![deny(missing_docs)]
16#![deny(unsafe_code)]
17
18pub use rust_toolchain::channel::{Beta, Nightly, Stable};
20use std::cmp;
21use std::fmt::Debug;
22
23pub mod date {
27 pub use rust_toolchain::Date;
28}
29pub mod toolchain {
31 pub use rust_toolchain::{Channel, Component, RustVersion, Target, Toolchain};
32}
33
34pub mod version;
36
37#[derive(Clone, Debug)]
46pub struct RustRelease<V: Debug, C = ()> {
47 pub version: V,
56 pub release_date: Option<date::Date>,
60 pub toolchains: Vec<toolchain::Toolchain>,
64 pub context: C,
66}
67
68impl<V: PartialEq + Debug, C> PartialEq for RustRelease<V, C> {
69 fn eq(&self, other: &Self) -> bool {
70 self.version.eq(&other.version)
71 }
72}
73
74impl<V: Eq + Debug, C> Eq for RustRelease<V, C> {}
75
76impl<V: PartialOrd + Debug, C> PartialOrd for RustRelease<V, C> {
77 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
78 self.version.partial_cmp(&other.version)
79 }
80}
81
82impl<V: Ord + Debug, C> Ord for RustRelease<V, C> {
83 fn cmp(&self, other: &Self) -> cmp::Ordering {
84 self.version.cmp(&other.version)
85 }
86}
87
88impl<V: Debug> RustRelease<V, ()> {
89 pub fn new(
92 version: V,
93 release_date: Option<rust_toolchain::Date>,
94 toolchains: impl IntoIterator<Item = toolchain::Toolchain>,
95 ) -> Self {
96 Self {
97 version,
98 release_date,
99 toolchains: toolchains.into_iter().collect(),
100 context: (),
101 }
102 }
103}
104
105impl<V: Debug, C> RustRelease<V, C> {
106 pub fn new_with_context(
114 version: V,
115 release_date: Option<rust_toolchain::Date>,
116 toolchains: impl IntoIterator<Item = toolchain::Toolchain>,
117 context: C,
118 ) -> Self {
119 Self {
120 version,
121 release_date,
122 toolchains: toolchains.into_iter().collect(),
123 context,
124 }
125 }
126 pub fn version(&self) -> &V {
136 &self.version
137 }
138
139 pub fn version_mut(&mut self) -> &mut V {
149 &mut self.version
150 }
151
152 pub fn release_date(&self) -> Option<&date::Date> {
154 self.release_date.as_ref()
155 }
156
157 pub fn release_date_mut(&mut self) -> Option<&mut date::Date> {
159 self.release_date.as_mut()
160 }
161
162 pub fn toolchains(&self) -> &Vec<toolchain::Toolchain> {
164 &self.toolchains
165 }
166
167 pub fn toolchains_mut(&mut self) -> &mut Vec<toolchain::Toolchain> {
169 &mut self.toolchains
170 }
171
172 pub fn toolchains_iter(&self) -> impl Iterator<Item = &toolchain::Toolchain> {
174 self.toolchains.iter()
175 }
176
177 pub fn context(&self) -> &C {
179 &self.context
180 }
181
182 pub fn context_mut(&mut self) -> &mut C {
184 &mut self.context
185 }
186}
187
188#[derive(Clone, Debug, Eq, PartialEq)]
193pub enum ReleaseVersion {
194 Stable(Stable),
196 Beta(Beta),
198 Nightly(Nightly),
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205 use crate::toolchain::Toolchain;
206 use rust_toolchain::RustVersion;
207 use std::collections::HashSet;
208
209 fn fake_tc(stable: Stable, date: Option<rust_toolchain::Date>) -> Toolchain {
211 Toolchain::new(
212 rust_toolchain::Channel::Stable(stable),
213 date,
214 rust_toolchain::Target::host(),
215 HashSet::new(),
216 HashSet::new(),
217 )
218 }
219
220 #[yare::parameterized(
221 some = { Some(rust_toolchain::Date::new(2024, 1, 1)) },
222 none = { None },
223 )]
224 fn can_instantiate_deux(date: Option<rust_toolchain::Date>) {
225 let stable = Stable {
226 version: RustVersion::new(1, 82, 0),
227 };
228 let version = ReleaseVersion::Stable(stable.clone());
229 let release = RustRelease::new(version, date.clone(), vec![fake_tc(stable, date)]);
230
231 let target_date = release.toolchains_iter().next().unwrap().date();
232
233 assert_eq!(release.release_date(), target_date);
234 }
235
236 #[test]
237 fn version() {
238 let stable = Stable::new(1, 82, 0);
239 let release = RustRelease::new(stable.clone(), None, vec![fake_tc(stable.clone(), None)]);
240
241 assert_eq!(release.version(), &stable);
242 }
243
244 #[test]
245 fn version_mut() {
246 let stable = Stable::new(1, 82, 0);
247 let mut release =
248 RustRelease::new(stable.clone(), None, vec![fake_tc(stable.clone(), None)]);
249
250 assert_eq!(release.version(), &stable);
251 let replacement = Stable::new(9, 9, 9);
252 *release.version_mut() = replacement.clone();
253
254 assert_eq!(release.version(), &replacement);
255 }
256
257 #[test]
258 fn release_date() {
259 let stable = Stable::new(1, 82, 0);
260 let date = rust_toolchain::Date::new(2026, 12, 12);
261 let release = RustRelease::new(
262 stable.clone(),
263 Some(date.clone()),
264 vec![fake_tc(stable.clone(), Some(date.clone()))],
265 );
266
267 assert_eq!(release.release_date().unwrap(), &date);
268 }
269
270 #[test]
271 fn release_date_mut() {
272 let stable = Stable::new(1, 82, 0);
273 let date = rust_toolchain::Date::new(2026, 12, 12);
274 let mut release = RustRelease::new(
275 stable.clone(),
276 Some(date.clone()),
277 vec![fake_tc(stable.clone(), Some(date.clone()))],
278 );
279
280 assert_eq!(release.release_date().unwrap(), &date);
281 let replacement = rust_toolchain::Date::new(2026, 05, 22);
282 release.release_date_mut().replace(&mut replacement.clone());
283
284 assert_eq!(release.release_date().unwrap(), &date);
285 }
286
287 #[test]
288 fn toolchains() {
289 let stable1 = Stable::new(1, 82, 0);
290 let stable2 = Stable::new(1, 83, 0);
291 let date = rust_toolchain::Date::new(2026, 12, 12);
292 let toolchains = vec![
295 fake_tc(stable1.clone(), Some(date.clone())),
296 fake_tc(stable2.clone(), None),
297 ];
298
299 let release = RustRelease::new(stable1.clone(), None, toolchains.clone());
300
301 assert_eq!(release.toolchains(), &toolchains);
302 }
303
304 #[test]
305 fn toolchains_mut() {
306 let stable1 = Stable::new(1, 82, 0);
307 let stable2 = Stable::new(1, 83, 0);
308 let date = rust_toolchain::Date::new(2026, 12, 12);
309 let toolchains = vec![
312 fake_tc(stable1.clone(), Some(date.clone())),
313 fake_tc(stable2.clone(), None),
314 ];
315 let mut release = RustRelease::new(stable1.clone(), None, toolchains.clone());
316
317 assert_eq!(release.toolchains(), &toolchains);
318
319 let stable3 = Stable::new(9, 9, 9);
320 let extension = fake_tc(stable3, None);
321 release.toolchains_mut().push(extension.clone());
322
323 let expected = [toolchains, vec![extension]].concat();
324 assert_eq!(release.toolchains(), &expected);
325 }
326
327 #[test]
328 fn context() {
329 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
330 enum Checksum {
331 Crc32(u32),
332 }
333
334 struct MyContext {
335 checksum: Checksum,
336 }
337
338 let stable = Stable::new(1, 82, 0);
339 let date = rust_toolchain::Date::new(2026, 12, 12);
340 let release = RustRelease::new_with_context(
341 stable.clone(),
342 None,
343 vec![fake_tc(stable.clone(), Some(date.clone()))],
344 MyContext {
345 checksum: Checksum::Crc32(0x00000000),
346 },
347 );
348
349 assert_eq!(release.context().checksum, Checksum::Crc32(0x00000000));
350 }
351
352 #[test]
353 fn context_mut() {
354 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
355 enum Checksum {
356 Crc32(u32),
357 }
358
359 struct MyContext {
360 checksum: Checksum,
361 }
362
363 let stable = Stable::new(1, 82, 0);
364 let date = rust_toolchain::Date::new(2026, 12, 12);
365 let mut release = RustRelease::new_with_context(
366 stable.clone(),
367 None,
368 vec![fake_tc(stable.clone(), Some(date.clone()))],
369 MyContext {
370 checksum: Checksum::Crc32(0x00000000),
371 },
372 );
373
374 assert_eq!(release.context().checksum, Checksum::Crc32(0x00000000));
375
376 let replacement = MyContext {
377 checksum: Checksum::Crc32(0xFFFFFFFF),
378 };
379 *release.context_mut() = replacement;
380
381 assert_eq!(release.context.checksum, Checksum::Crc32(0xFFFFFFFF));
382 }
383
384 #[test]
385 fn pattern_match() {
386 let stable = Stable::new(1, 82, 0);
387 let release = RustRelease::new(stable.clone(), None, vec![fake_tc(stable.clone(), None)]);
388
389 let RustRelease {
390 version,
391 toolchains,
392 context,
393 ..
394 } = release;
395
396 assert_eq!(version, stable);
397 assert_eq!(toolchains.len(), 1);
398 assert_eq!(context, ());
399 }
400}