Skip to content

Commit 5e1b13a

Browse files
maxdeviantlpil
authored andcommitted
Fix PubGrub's bump to be strictly higher for pre-releases
1 parent 907f240 commit 5e1b13a

File tree

2 files changed

+115
-1
lines changed

2 files changed

+115
-1
lines changed

src/version.rs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,96 @@ impl pubgrub::version::Version for Version {
180180
}
181181

182182
fn bump(&self) -> Self {
183-
self.bump_patch()
183+
if self.is_pre() {
184+
let mut pre = self.pre.clone();
185+
let last_component = pre
186+
.last_mut()
187+
// This `.expect` is safe, as we know there to be at least
188+
// one pre-release component.
189+
.expect("no pre-release components");
190+
191+
match last_component {
192+
Identifier::Numeric(pre) => *pre += 1,
193+
Identifier::AlphaNumeric(pre) => {
194+
let mut segments = split_alphanumeric(&pre);
195+
let last_segment = segments.last_mut().unwrap();
196+
197+
match last_segment {
198+
AlphaOrNumeric::Numeric(n) => *n += 1,
199+
AlphaOrNumeric::Alpha(alpha) => {
200+
// We should potentially be smarter about this (for instance, pick the next letter in the
201+
// alphabetic sequence), however, this seems like it could be quite a bit more complex.
202+
alpha.push_str("1")
203+
}
204+
}
205+
206+
*pre = segments
207+
.into_iter()
208+
.map(|segment| match segment {
209+
AlphaOrNumeric::Alpha(segment) => segment,
210+
AlphaOrNumeric::Numeric(segment) => segment.to_string(),
211+
})
212+
.collect::<Vec<_>>()
213+
.join("");
214+
}
215+
}
216+
217+
Self {
218+
major: self.major,
219+
minor: self.minor,
220+
patch: self.patch,
221+
pre,
222+
build: None,
223+
}
224+
} else {
225+
self.bump_patch()
226+
}
227+
}
228+
}
229+
230+
enum AlphaOrNumeric {
231+
Alpha(String),
232+
Numeric(u32),
233+
}
234+
235+
/// Splits the given string into alphabetic and numeric segments.
236+
fn split_alphanumeric(str: &str) -> Vec<AlphaOrNumeric> {
237+
let mut segments = Vec::new();
238+
let mut current_segment = String::new();
239+
let mut previous_char_was_numeric = None;
240+
241+
for char in str.chars() {
242+
let is_numeric = char.is_ascii_digit();
243+
match previous_char_was_numeric {
244+
Some(previous_char_was_numeric) if previous_char_was_numeric == is_numeric => {
245+
current_segment.push(char)
246+
}
247+
_ => {
248+
if !current_segment.is_empty() {
249+
if current_segment.chars().any(|char| char.is_ascii_digit()) {
250+
segments.push(AlphaOrNumeric::Numeric(current_segment.parse().unwrap()));
251+
} else {
252+
segments.push(AlphaOrNumeric::Alpha(current_segment));
253+
}
254+
255+
current_segment = String::new();
256+
}
257+
258+
current_segment.push(char);
259+
previous_char_was_numeric = Some(is_numeric);
260+
}
261+
}
184262
}
263+
264+
if !current_segment.is_empty() {
265+
if current_segment.chars().any(|char| char.is_ascii_digit()) {
266+
segments.push(AlphaOrNumeric::Numeric(current_segment.parse().unwrap()));
267+
} else {
268+
segments.push(AlphaOrNumeric::Alpha(current_segment));
269+
}
270+
}
271+
272+
segments
185273
}
186274

187275
impl<'a> TryFrom<&'a str> for Version {

src/version/tests.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use std::{
33
collections::HashMap,
44
};
55

6+
use pubgrub::version::Version as _;
7+
68
use crate::{ApiError, Release, RetirementReason, RetirementStatus};
79

810
use super::{
@@ -389,6 +391,30 @@ assert_order!(ord_pre_smaller_than_zero_flip, "1.0.0-rc1", Less, "1.0.0");
389391

390392
assert_order!(ord_pre_rc1_2, "1.0.0-rc1", Less, "1.0.0-rc2");
391393

394+
#[test]
395+
fn test_pubgrub_bump_patch() {
396+
assert_eq!(
397+
Version::parse("1.0.0").unwrap().bump(),
398+
Version::parse("1.0.1").unwrap()
399+
);
400+
}
401+
402+
#[test]
403+
fn test_pubgrub_bump_prerelease_ending_with_a_number() {
404+
assert_eq!(
405+
Version::parse("1.0.0-rc2").unwrap().bump(),
406+
Version::parse("1.0.0-rc3").unwrap()
407+
);
408+
}
409+
410+
#[test]
411+
fn test_pubgrub_bump_prerelease_ending_with_a_letter() {
412+
assert_eq!(
413+
Version::parse("1.0.0-rc2a").unwrap().bump(),
414+
Version::parse("1.0.0-rc2a1").unwrap()
415+
);
416+
}
417+
392418
struct Remote {
393419
deps: HashMap<String, Package>,
394420
}

0 commit comments

Comments
 (0)