cuddle_please_misc/versioning/
next_version.rs

1use semver::{Prerelease, Version};
2
3use super::conventional_parse::VersionIncrement;
4
5pub trait NextVersion {
6    fn next<I>(&self, commits: I) -> Self
7    where
8        I: IntoIterator,
9        I::Item: AsRef<str>;
10}
11
12impl NextVersion for Version {
13    fn next<I>(&self, commits: I) -> Self
14    where
15        I: IntoIterator,
16        I::Item: AsRef<str>,
17    {
18        let increment = VersionIncrement::from(self, commits);
19
20        match increment {
21            Some(increment) => match increment {
22                VersionIncrement::Major => Self {
23                    major: self.major + 1,
24                    minor: 0,
25                    patch: 0,
26                    pre: Prerelease::EMPTY,
27                    ..self.clone()
28                },
29                VersionIncrement::Minor => Self {
30                    minor: self.minor + 1,
31                    patch: 0,
32                    pre: Prerelease::EMPTY,
33                    ..self.clone()
34                },
35                VersionIncrement::Patch => Self {
36                    patch: self.patch + 1,
37                    pre: Prerelease::EMPTY,
38                    ..self.clone()
39                },
40                VersionIncrement::Prerelease => Self {
41                    pre: {
42                        let release = &self.pre;
43                        let release_version = match release.rsplit_once('.') {
44                            Some((tag, version)) => match version.parse::<usize>() {
45                                Ok(version) => format!("{tag}.{}", version + 1),
46                                Err(_) => format!("{tag}.1"),
47                            },
48                            None => format!("{release}.1"),
49                        };
50                        Prerelease::new(&release_version).expect("prerelease is not valid semver")
51                    },
52                    ..self.clone()
53                },
54            },
55            None => self.clone(),
56        }
57    }
58}
59
60#[cfg(test)]
61mod test {
62    use semver::Version;
63    use tracing_test::traced_test;
64
65    use crate::versioning::next_version::NextVersion;
66
67    #[test]
68    #[traced_test]
69    fn is_no_bump() {
70        let version = Version::parse("0.0.0-prerelease").unwrap();
71        let commits: Vec<&str> = vec![];
72
73        let actual = version.next(commits);
74
75        assert_eq!("0.0.0-prerelease", actual.to_string())
76    }
77
78    #[test]
79    #[traced_test]
80    fn is_prerelease_initial() {
81        let version = Version::parse("0.0.0-prerelease").unwrap();
82        let commits: Vec<&str> = vec!["feat: something"];
83
84        let actual = version.next(commits);
85
86        assert_eq!("0.0.0-prerelease.1", actual.to_string())
87    }
88
89    #[test]
90    #[traced_test]
91    fn is_prerelease_invalid() {
92        let version = Version::parse("0.0.0-prerelease.invalid").unwrap();
93        let commits: Vec<&str> = vec!["feat: something"];
94
95        let actual = version.next(commits);
96
97        assert_eq!("0.0.0-prerelease.1", actual.to_string())
98    }
99
100    #[test]
101    #[traced_test]
102    fn is_prerelease_next() {
103        let version = Version::parse("0.0.0-prerelease.1").unwrap();
104        let commits: Vec<&str> = vec!["feat: something"];
105
106        let actual = version.next(commits);
107
108        assert_eq!("0.0.0-prerelease.2", actual.to_string())
109    }
110
111    #[test]
112    #[traced_test]
113    fn is_patch() {
114        let version = Version::parse("0.0.0").unwrap();
115        let commits: Vec<&str> = vec!["fix: something"];
116
117        let actual = version.next(commits);
118
119        assert_eq!("0.0.1", actual.to_string())
120    }
121
122    #[test]
123    #[traced_test]
124    fn is_minor() {
125        let version = Version::parse("0.1.0").unwrap();
126        let commits: Vec<&str> = vec!["feat: something"];
127
128        let actual = version.next(commits);
129
130        assert_eq!("0.2.0", actual.to_string())
131    }
132    #[test]
133    #[traced_test]
134    fn is_minor_clears_patch() {
135        let version = Version::parse("0.1.1").unwrap();
136        let commits: Vec<&str> = vec!["feat: something"];
137
138        let actual = version.next(commits);
139
140        assert_eq!("0.2.0", actual.to_string())
141    }
142    #[test]
143    #[traced_test]
144    fn is_major() {
145        let version = Version::parse("0.0.0").unwrap();
146        let commits: Vec<&str> = vec![
147            "feat: something
148            
149            BREAKING CHANGE: something",
150        ];
151
152        let actual = version.next(commits);
153
154        assert_eq!("1.0.0", actual.to_string())
155    }
156    #[test]
157    #[traced_test]
158    fn is_major_clears_minor_patch() {
159        let version = Version::parse("1.2.3").unwrap();
160        let commits: Vec<&str> = vec![
161            "feat: something
162            
163            BREAKING CHANGE: something",
164        ];
165
166        let actual = version.next(commits);
167
168        assert_eq!("2.0.0", actual.to_string())
169    }
170}