1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use rust_releases_core::Release;
use std::iter;

/// An iterator over the latest stable releases, with only the latest patch version included.
/// For example, if the ordered set of releases given consists of
/// `{"1.40.2", "1.40.1", "1.40.0", "1.39.0", "1.38.1", "1.38.0"}`, the iterator will return in
/// order `{"1.40.2", "1.39.0", "1.38.1"}`.
///
/// NB: Assumes releases are ordered from most to least recent on iterator initialization.
pub struct LatestStableReleasesIterator<I: Iterator<Item = Release>> {
    pub(crate) iter: iter::Peekable<I>,
}

impl<I: Iterator<Item = Release>> Iterator for LatestStableReleasesIterator<I> {
    type Item = I::Item;

    fn next(&mut self) -> Option<Self::Item> {
        let current = self.iter.next();

        current.map(|it| {
            let minor = it.version().minor;

            while let Some(release) = self.iter.peek() {
                if release.version().minor == minor {
                    self.iter.next();
                } else {
                    break;
                }
            }

            it
        })
    }
}

/// Trait to transform any iterator over [`Release`] into a [`LatestStableReleasesIterator`]
///
/// [`Release`]: crate::Release
/// [`LatestStableReleasesIterator`]: core::linear::LatestStableReleasesIterator
pub trait LatestStableReleases: Iterator<Item = Release> + Sized {
    /// Consume the given iterator over [`Release`] items, into a [`LatestStableReleasesIterator`].
    fn latest_stable_releases(self) -> LatestStableReleasesIterator<Self>;
}

impl<I: Iterator<Item = Release>> LatestStableReleases for I {
    fn latest_stable_releases(self) -> LatestStableReleasesIterator<I> {
        LatestStableReleasesIterator {
            iter: self.peekable(),
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::linear::LatestStableReleases;
    use crate::Release;
    use rust_releases_core::semver;

    struct MyTestStruct {
        vec: Vec<Release>,
    }

    impl MyTestStruct {
        fn releases(&self) -> &[Release] {
            &self.vec
        }

        fn into_latest_patch(self) -> Self {
            Self {
                vec: self.vec.into_iter().latest_stable_releases().collect(),
            }
        }
    }

    #[test]
    fn use_case_test() {
        let releases = vec![
            Release::new_stable(semver::Version::new(1, 40, 2)),
            Release::new_stable(semver::Version::new(1, 40, 1)),
            Release::new_stable(semver::Version::new(1, 40, 0)),
            Release::new_stable(semver::Version::new(1, 39, 0)),
            Release::new_stable(semver::Version::new(1, 38, 1)),
            Release::new_stable(semver::Version::new(1, 38, 0)),
        ];

        let system_under_test = MyTestStruct { vec: releases };

        // pre check
        assert_eq!(system_under_test.releases().len(), 6);
        assert_eq!(
            system_under_test.releases()[0],
            Release::new_stable(semver::Version::new(1, 40, 2))
        );
        assert_eq!(
            system_under_test.releases()[5],
            Release::new_stable(semver::Version::new(1, 38, 0))
        );

        // perform action (moves bind, and returns Self)
        let system_under_test = system_under_test.into_latest_patch();

        assert_eq!(system_under_test.releases().len(), 3);
        assert_eq!(
            system_under_test.releases()[0],
            Release::new_stable(semver::Version::new(1, 40, 2))
        );
        assert_eq!(
            system_under_test.releases()[1],
            Release::new_stable(semver::Version::new(1, 39, 0))
        );
        assert_eq!(
            system_under_test.releases()[2],
            Release::new_stable(semver::Version::new(1, 38, 1))
        );
    }
}