Skip to main content

rust_releases/
linear.rs

1use rust_releases_core::Release;
2use std::iter;
3
4/// An iterator over the latest stable releases, with only the latest patch version included.
5/// For example, if the ordered set of releases given consists of
6/// `{"1.40.2", "1.40.1", "1.40.0", "1.39.0", "1.38.1", "1.38.0"}`, the iterator will return in
7/// order `{"1.40.2", "1.39.0", "1.38.1"}`.
8///
9/// NB: Assumes releases are ordered from most to least recent on iterator initialization.
10pub struct LatestStableReleasesIterator<I: Iterator<Item = Release>> {
11    pub(crate) iter: iter::Peekable<I>,
12}
13
14impl<I: Iterator<Item = Release>> Iterator for LatestStableReleasesIterator<I> {
15    type Item = I::Item;
16
17    fn next(&mut self) -> Option<Self::Item> {
18        let current = self.iter.next();
19
20        #[allow(clippy::manual_inspect)]
21        current.map(|it| {
22            let minor = it.version().minor;
23
24            while let Some(release) = self.iter.peek() {
25                if release.version().minor == minor {
26                    self.iter.next();
27                } else {
28                    break;
29                }
30            }
31
32            it
33        })
34    }
35}
36
37/// Trait to transform any iterator over [`Release`] into a [`LatestStableReleasesIterator`]
38///
39/// [`Release`]: crate::Release
40/// [`LatestStableReleasesIterator`]: core::linear::LatestStableReleasesIterator
41pub trait LatestStableReleases: Iterator<Item = Release> + Sized {
42    /// Consume the given iterator over [`Release`] items, into a [`LatestStableReleasesIterator`].
43    fn latest_stable_releases(self) -> LatestStableReleasesIterator<Self>;
44}
45
46impl<I: Iterator<Item = Release>> LatestStableReleases for I {
47    fn latest_stable_releases(self) -> LatestStableReleasesIterator<I> {
48        LatestStableReleasesIterator {
49            iter: self.peekable(),
50        }
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use crate::linear::LatestStableReleases;
57    use crate::Release;
58    use rust_releases_core::semver;
59
60    struct MyTestStruct {
61        vec: Vec<Release>,
62    }
63
64    impl MyTestStruct {
65        fn releases(&self) -> &[Release] {
66            &self.vec
67        }
68
69        fn into_latest_patch(self) -> Self {
70            Self {
71                vec: self.vec.into_iter().latest_stable_releases().collect(),
72            }
73        }
74    }
75
76    #[test]
77    fn use_case_test() {
78        let releases = vec![
79            Release::new_stable(semver::Version::new(1, 40, 2)),
80            Release::new_stable(semver::Version::new(1, 40, 1)),
81            Release::new_stable(semver::Version::new(1, 40, 0)),
82            Release::new_stable(semver::Version::new(1, 39, 0)),
83            Release::new_stable(semver::Version::new(1, 38, 1)),
84            Release::new_stable(semver::Version::new(1, 38, 0)),
85        ];
86
87        let system_under_test = MyTestStruct { vec: releases };
88
89        // pre check
90        assert_eq!(system_under_test.releases().len(), 6);
91        assert_eq!(
92            system_under_test.releases()[0],
93            Release::new_stable(semver::Version::new(1, 40, 2))
94        );
95        assert_eq!(
96            system_under_test.releases()[5],
97            Release::new_stable(semver::Version::new(1, 38, 0))
98        );
99
100        // perform action (moves bind, and returns Self)
101        let system_under_test = system_under_test.into_latest_patch();
102
103        assert_eq!(system_under_test.releases().len(), 3);
104        assert_eq!(
105            system_under_test.releases()[0],
106            Release::new_stable(semver::Version::new(1, 40, 2))
107        );
108        assert_eq!(
109            system_under_test.releases()[1],
110            Release::new_stable(semver::Version::new(1, 39, 0))
111        );
112        assert_eq!(
113            system_under_test.releases()[2],
114            Release::new_stable(semver::Version::new(1, 38, 1))
115        );
116    }
117}