1use std::{collections::BTreeMap, fmt::Debug, str::FromStr};
2
3use tracing::debug;
4
5use super::{
6 Label, PreVersion, Prerelease, Rule, StableVersion, Version, prerelease_map::PrereleaseMap,
7};
8use crate::semver::rule::Stable;
9
10#[derive(Clone, Debug, Default, Eq, PartialEq)]
16pub struct PackageVersions {
17 stable: Option<StableVersion>,
18 prereleases: Prereleases,
19}
20
21type Prereleases = BTreeMap<StableVersion, PrereleaseMap>;
22
23impl PackageVersions {
24 pub fn from_tags<S: AsRef<str> + Debug>(prefix: Option<&str>, all_tags: &[S]) -> Self {
32 let pattern = prefix
33 .as_ref()
34 .map_or_else(|| String::from("v"), |prefix| format!("{prefix}/v"));
35 let mut tags = all_tags
36 .iter()
37 .filter(|tag| tag.as_ref().starts_with(&pattern))
38 .peekable();
39
40 if tags.peek().is_none() {
41 debug!("No tags found starting with {pattern}");
42 }
43
44 let mut current_versions = Self::default();
45 for tag in tags {
46 let version_string = tag.as_ref().replacen(&pattern, "", 1);
47 if let Ok(version) = Version::from_str(version_string.as_str()) {
48 match version {
49 Version::Stable(stable) => {
50 current_versions.stable = Some(stable);
51 break; }
53 Version::Pre(_) => {
54 current_versions.update_version(version);
55 }
56 }
57 }
58 }
59
60 current_versions
61 }
62
63 #[must_use]
65 pub fn into_latest(mut self) -> Option<Version> {
66 self.prereleases
67 .pop_last()
68 .map(|(stable_component, pres)| {
69 let pre_component = pres.into_last();
70 Version::Pre(PreVersion {
71 stable_component,
72 pre_component,
73 })
74 })
75 .or_else(|| self.stable.map(Version::Stable))
76 }
77
78 pub(crate) fn update_version(&mut self, version: Version) {
85 match version {
86 Version::Stable(new) => {
87 if self.stable.is_some_and(|it| it >= new) {
88 return;
89 }
90 self.stable = Some(new);
91 self.prereleases.clear();
92 }
93 Version::Pre(PreVersion {
94 stable_component,
95 pre_component,
96 }) => {
97 let recorded_pre = self
98 .prereleases
99 .get(&stable_component)
100 .and_then(|pres| pres.get(&pre_component.label));
101 if let Some(recorded_pre) = recorded_pre {
102 if recorded_pre >= &pre_component {
103 return;
104 }
105 }
106 if let Some(labels) = self.prereleases.get_mut(&stable_component) {
107 labels.insert(pre_component);
108 } else {
109 self.prereleases
110 .insert(stable_component, PrereleaseMap::new(pre_component));
111 }
112 }
113 }
114 }
115
116 pub fn bump(&mut self, rule: Rule) -> Result<Version, PreReleaseNotFound> {
129 match (rule, self.stable) {
130 (Rule::Stable(rule), Some(stable)) => {
131 let version: Version = bump_stable(stable, rule).into();
132 self.update_version(version.clone());
133 Ok(version)
134 }
135 (Rule::Stable(_), None) => {
136 let version: Version = self
140 .prereleases
141 .pop_last()
142 .map(|(version, _pre)| version)
143 .unwrap_or_default()
144 .into();
145 self.update_version(version.clone());
146 Ok(version)
147 }
148 (Rule::Pre { label, stable_rule }, _) => Ok(self.bump_pre(label, stable_rule)),
149 (Rule::Release, _) => {
150 let version: Version = self
151 .prereleases
152 .pop_last()
153 .map(|(version, _pre)| version)
154 .ok_or(PreReleaseNotFound)?
155 .into();
156 self.update_version(version.clone());
157 Ok(version)
158 }
159 }
160 }
161
162 #[must_use]
163 pub fn stable(&self) -> Option<StableVersion> {
164 self.stable
165 }
166
167 fn bump_pre(&mut self, label: Label, stable_rule: Stable) -> Version {
169 debug!("Pre-release label {label} selected. Determining next stable version...");
170 let stable_component = if let Some(stable) = self.stable {
171 bump_stable(stable, stable_rule)
172 } else {
173 self.prereleases
174 .last_key_value()
175 .map(|(stable, _)| *stable)
176 .unwrap_or_default()
177 };
178 let pre_version = self
179 .prereleases
180 .get(&stable_component)
181 .and_then(|pres| {
182 pres.get(&label).map(|pre| {
183 debug!("Found existing pre-release version {pre}");
184 pre.version + 1
185 })
186 })
187 .unwrap_or_default();
188 let pre = Prerelease::new(label, pre_version);
189 if pre_version == 0 {
190 debug!("No existing pre-release version found; creating {pre}");
191 }
192
193 self.prereleases.clear();
194
195 let version = Version::Pre(PreVersion {
196 stable_component,
197 pre_component: pre,
198 });
199 self.update_version(version.clone());
200 version
201 }
202}
203
204fn bump_stable(version: StableVersion, rule: Stable) -> StableVersion {
205 let is_0 = version.major == 0;
206 match (rule, is_0) {
207 (Stable::Major, false) => {
208 let new = version.increment_major();
209 debug!("Using MAJOR rule to bump from {version} to {new}");
210 new
211 }
212 (Stable::Minor, false) => {
213 let new = version.increment_minor();
214 debug!("Using MINOR rule to bump from {version} to {new}");
215 new
216 }
217 (Stable::Major, true) => {
218 let new = version.increment_minor();
219 debug!(
220 "Rule is MAJOR, but major component is 0. Bumping minor component from {version} to {new}"
221 );
222 new
223 }
224 (Stable::Minor, true) => {
225 let new = version.increment_patch();
226 debug!(
227 "Rule is MINOR, but major component is 0. Bumping patch component from {version} to {new}"
228 );
229 new
230 }
231 (Stable::Patch, _) => {
232 let new = version.increment_patch();
233 debug!("Using PATCH rule to bump from {version} to {new}");
234 new
235 }
236 }
237}
238
239#[derive(Debug, thiserror::Error)]
256#[error("No prerelease version found, but a Release rule was requested")]
257pub struct PreReleaseNotFound;
258
259impl From<StableVersion> for PackageVersions {
260 fn from(version: StableVersion) -> Self {
261 Self {
262 stable: Some(version),
263 prereleases: BTreeMap::new(),
264 }
265 }
266}
267
268impl From<Version> for PackageVersions {
269 fn from(version: Version) -> Self {
270 let mut new = Self::default();
271 new.update_version(version);
272 new
273 }
274}
275
276#[cfg(test)]
277mod test_from_tags {
278 use std::str::FromStr;
279
280 use pretty_assertions::assert_eq;
281
282 use crate::semver::{PackageVersions, Prerelease, StableVersion, Version};
283 #[test]
284 fn collect_all_newer_pre_releases() {
285 let tags = [
286 "v2.0.0-alpha.0",
287 "v1.3.0-beta.0",
288 "v1.3.0-alpha.1",
289 "v1.3.0-alpha.0",
290 "v1.2.4-rc.0",
291 "v1.2.3",
292 ]
293 .map(String::from);
294
295 let versions = PackageVersions::from_tags(None, &tags);
296
297 assert_eq!(
298 versions.stable().unwrap(),
299 StableVersion {
300 major: 1,
301 minor: 2,
302 patch: 3
303 }
304 );
305
306 assert_eq!(
307 versions.clone().into_latest(),
308 Version::from_str("2.0.0-alpha.0").ok()
309 );
310 assert_eq!(
311 *versions
312 .prereleases
313 .get(&StableVersion {
314 major: 1,
315 minor: 3,
316 patch: 0
317 })
318 .unwrap()
319 .get(&"alpha".into())
320 .unwrap(),
321 Prerelease::new("alpha".into(), 1)
322 );
323
324 assert_eq!(
325 *versions
326 .prereleases
327 .get(&StableVersion {
328 major: 1,
329 minor: 3,
330 patch: 0
331 })
332 .unwrap()
333 .get(&"beta".into())
334 .unwrap(),
335 Prerelease::new("beta".into(), 0)
336 );
337 }
338}
339
340#[cfg(test)]
341mod test_bump {
342 use std::str::FromStr;
343
344 use super::*;
345 use crate::semver::{Rule::*, StableRule::*};
346
347 #[test]
348 fn major() {
349 let mut versions: PackageVersions = Version::new(1, 2, 3, None).into();
350 versions.bump(Stable(Major)).unwrap();
351
352 assert_eq!(versions.into_latest().unwrap(), Version::new(2, 0, 0, None));
353 }
354
355 #[test]
356 fn major_0() {
357 let mut versions = PackageVersions::from(Version::new(0, 1, 2, None));
358 versions.bump(Stable(Major)).unwrap();
359
360 assert_eq!(versions.into_latest().unwrap(), Version::new(0, 2, 0, None));
361 }
362
363 #[test]
364 fn major_pre_only() {
365 let mut versions = PackageVersions::from_tags(None, &["v1.0.0-rc.0"]);
366 versions.bump(Stable(Major)).unwrap();
367
368 assert_eq!(versions.into_latest().unwrap(), Version::new(1, 0, 0, None));
369 }
370
371 #[test]
372 fn major_unset() {
373 let mut versions = PackageVersions::default();
374 versions.bump(Stable(Major)).unwrap();
375
376 assert_eq!(versions.into_latest().unwrap(), Version::new(0, 0, 0, None));
377 }
378
379 #[test]
380 fn major_after_pre() {
381 for pre_version in ["1.2.4-rc.0", "1.3.0-rc.0", "2.0.0-rc.0"] {
382 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
383 versions.update_version(Version::from_str(pre_version).unwrap());
384 versions.bump(Stable(Major)).unwrap();
385
386 assert_eq!(versions.into_latest().unwrap(), Version::new(2, 0, 0, None));
387 }
388 }
389
390 #[test]
391 fn minor() {
392 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
393 versions.bump(Stable(Minor)).unwrap();
394
395 assert_eq!(versions.into_latest().unwrap(), Version::new(1, 3, 0, None));
396 }
397
398 #[test]
399 fn minor_0() {
400 let mut versions = PackageVersions::from(Version::new(0, 1, 2, None));
401 versions.bump(Stable(Minor)).unwrap();
402
403 assert_eq!(versions.into_latest().unwrap(), Version::new(0, 1, 3, None));
404 }
405
406 #[test]
407 fn minor_pre_only() {
408 let mut versions = PackageVersions::from_tags(None, &["v1.0.0-rc.0"]);
409 versions.bump(Stable(Minor)).unwrap();
410 assert_eq!(versions.into_latest().unwrap(), Version::new(1, 0, 0, None));
411 }
412
413 #[test]
414 fn minor_unset() {
415 let mut versions = PackageVersions::default();
416 versions.bump(Stable(Minor)).unwrap();
417
418 assert_eq!(versions.into_latest().unwrap(), Version::new(0, 0, 0, None));
419 }
420
421 #[test]
422 fn minor_after_pre() {
423 for pre_version in ["1.2.4-rc.0", "1.3.0-rc.0"] {
424 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
425 versions.update_version(Version::from_str(pre_version).unwrap());
426 versions.bump(Stable(Minor)).unwrap();
427
428 assert_eq!(versions.into_latest().unwrap(), Version::new(1, 3, 0, None));
429 }
430 }
431
432 #[test]
433 fn patch() {
434 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
435 versions.bump(Stable(Patch)).unwrap();
436
437 assert_eq!(versions.into_latest().unwrap(), Version::new(1, 2, 4, None));
438 }
439
440 #[test]
441 fn patch_0() {
442 let mut versions = PackageVersions::from(Version::new(0, 1, 0, None));
443 versions.bump(Stable(Patch)).unwrap();
444
445 assert_eq!(versions.into_latest().unwrap(), Version::new(0, 1, 1, None));
446 }
447
448 #[test]
449 fn patch_pre_only() {
450 let mut versions = PackageVersions::from_tags(None, &["v1.0.0-rc.0"]);
451 versions.bump(Stable(Patch)).unwrap();
452 assert_eq!(versions.into_latest().unwrap(), Version::new(1, 0, 0, None));
453 }
454
455 #[test]
456 fn patch_unset() {
457 let mut versions = PackageVersions::default();
458 versions.bump(Stable(Patch)).unwrap();
459
460 assert_eq!(versions.into_latest().unwrap(), Version::new(0, 0, 0, None));
461 }
462
463 #[test]
464 fn patch_after_pre() {
465 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
466 versions.update_version(Version::from_str("1.2.4-rc.0").unwrap());
467 versions.bump(Stable(Patch)).unwrap();
468
469 assert_eq!(versions.into_latest().unwrap(), Version::new(1, 2, 4, None));
470 }
471
472 #[test]
473 fn pre() {
474 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
475 versions
476 .bump(Rule::Pre {
477 label: Label::from("rc"),
478 stable_rule: Minor,
479 })
480 .unwrap();
481
482 assert_eq!(versions.into_latest(), Version::from_str("1.3.0-rc.0").ok());
483 }
484
485 #[test]
486 fn pre_after_same_pre() {
487 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
488 versions.update_version(Version::from_str("1.3.0-rc.0").unwrap());
489 versions.update_version(Version::from_str("1.2.4-rc.1").unwrap());
490 versions.update_version(Version::from_str("2.0.0-rc.2").unwrap());
491 versions
492 .bump(Rule::Pre {
493 label: Label::from("rc"),
494 stable_rule: Minor,
495 })
496 .unwrap();
497
498 assert_eq!(versions.into_latest(), Version::from_str("1.3.0-rc.1").ok());
499 }
500
501 #[test]
502 fn pre_without_stable() {
503 let mut versions = PackageVersions::default();
504 versions.update_version(Version::from_str("1.3.0-rc.0").unwrap());
505 versions.update_version(Version::from_str("1.2.4-rc.1").unwrap());
506 versions.update_version(Version::from_str("2.0.0-rc.2").unwrap());
507 versions
508 .bump(Rule::Pre {
509 label: Label::from("rc"),
510 stable_rule: Minor,
511 })
512 .unwrap();
513
514 assert_eq!(versions.into_latest(), Version::from_str("2.0.0-rc.3").ok());
515 }
516
517 #[test]
518 fn pre_after_different_pre_version() {
519 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
520 versions.update_version(Version::from_str("1.2.4-beta.1").unwrap());
521 versions.update_version(Version::from_str("1.2.4-rc.0").unwrap());
522 versions
523 .bump(Rule::Pre {
524 label: Label::from("beta"),
525 stable_rule: Patch,
526 })
527 .unwrap();
528
529 assert_eq!(
530 versions.into_latest(),
531 Version::from_str("1.2.4-beta.2").ok()
532 );
533 }
534
535 #[test]
536 fn pre_after_different_pre_label() {
537 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
538 versions.update_version(Version::from_str("1.3.0-beta.0").unwrap());
539 versions
540 .bump(Rule::Pre {
541 label: Label::from("rc"),
542 stable_rule: Minor,
543 })
544 .unwrap();
545
546 assert_eq!(versions.into_latest(), Version::from_str("1.3.0-rc.0").ok());
547 }
548
549 #[test]
550 fn release() {
551 let mut versions = PackageVersions::default();
552 versions.update_version(Version::from_str("1.2.3-rc.0").unwrap());
553 versions.update_version(Version::from_str("1.2.4-rc.1").unwrap());
554 versions.update_version(Version::from_str("2.0.0-rc.2").unwrap());
555
556 versions.bump(Rule::Release).unwrap();
557
558 assert_eq!(versions.into_latest().unwrap(), Version::new(2, 0, 0, None));
559 }
560}