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 pub(crate) 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 latest(&self) -> Option<Version> {
66 self.prereleases
67 .last_key_value()
68 .map(|(stable_component, pres)| {
69 let pre_component = pres.last().clone();
70 Version::Pre(PreVersion {
71 stable_component: *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(crate) fn calculate_new_version(&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 Ok(version)
133 }
134 (Rule::Stable(_), None) => {
135 let version: Version = self
139 .prereleases
140 .last_key_value()
141 .map(|(version, _pre)| *version)
142 .unwrap_or_default()
143 .into();
144 Ok(version)
145 }
146 (Rule::Pre { label, stable_rule }, _) => Ok(self.calculate_pre(label, stable_rule)),
147 (Rule::Release, _) => {
148 let version: Version = self
149 .prereleases
150 .last_key_value()
151 .map(|(version, _pre)| (*version).into())
152 .ok_or(PreReleaseNotFound)?;
153 Ok(version)
154 }
155 }
156 }
157
158 #[must_use]
159 pub fn stable(&self) -> Option<StableVersion> {
160 self.stable
161 }
162
163 fn calculate_pre(&self, label: Label, stable_rule: Stable) -> Version {
165 debug!("Pre-release label {label} selected. Determining next stable version...");
166 let stable_component = if let Some(stable) = self.stable {
167 bump_stable(stable, stable_rule)
168 } else {
169 self.prereleases
170 .last_key_value()
171 .map(|(stable, _)| *stable)
172 .unwrap_or_default()
173 };
174 let pre_version = self
175 .prereleases
176 .get(&stable_component)
177 .and_then(|pres| {
178 pres.get(&label).map(|pre| {
179 debug!("Found existing pre-release version {pre}");
180 pre.version + 1
181 })
182 })
183 .unwrap_or_default();
184 let pre = Prerelease::new(label, pre_version);
185 if pre_version == 0 {
186 debug!("No existing pre-release version found; creating {pre}");
187 }
188
189 Version::Pre(PreVersion {
190 stable_component,
191 pre_component: pre,
192 })
193 }
194}
195
196fn bump_stable(version: StableVersion, rule: Stable) -> StableVersion {
197 let is_0 = version.major == 0;
198 match (rule, is_0) {
199 (Stable::Major, false) => {
200 let new = version.increment_major();
201 debug!("Using MAJOR rule to bump from {version} to {new}");
202 new
203 }
204 (Stable::Minor, false) => {
205 let new = version.increment_minor();
206 debug!("Using MINOR rule to bump from {version} to {new}");
207 new
208 }
209 (Stable::Major, true) => {
210 let new = version.increment_minor();
211 debug!(
212 "Rule is MAJOR, but major component is 0. Bumping minor component from {version} to {new}"
213 );
214 new
215 }
216 (Stable::Minor, true) => {
217 let new = version.increment_patch();
218 debug!(
219 "Rule is MINOR, but major component is 0. Bumping patch component from {version} to {new}"
220 );
221 new
222 }
223 (Stable::Patch, _) => {
224 let new = version.increment_patch();
225 debug!("Using PATCH rule to bump from {version} to {new}");
226 new
227 }
228 }
229}
230
231#[derive(Debug, thiserror::Error)]
248#[error("No prerelease version found, but a Release rule was requested")]
249pub struct PreReleaseNotFound;
250
251impl From<StableVersion> for PackageVersions {
252 fn from(version: StableVersion) -> Self {
253 Self {
254 stable: Some(version),
255 prereleases: BTreeMap::new(),
256 }
257 }
258}
259
260impl From<Version> for PackageVersions {
261 fn from(version: Version) -> Self {
262 let mut new = Self::default();
263 new.update_version(version);
264 new
265 }
266}
267
268#[cfg(test)]
269mod test_from_tags {
270 use std::str::FromStr;
271
272 use pretty_assertions::assert_eq;
273
274 use crate::semver::{PackageVersions, Prerelease, StableVersion, Version};
275 #[test]
276 fn collect_all_newer_pre_releases() {
277 let tags = [
278 "v2.0.0-alpha.0",
279 "v1.3.0-beta.0",
280 "v1.3.0-alpha.1",
281 "v1.3.0-alpha.0",
282 "v1.2.4-rc.0",
283 "v1.2.3",
284 ]
285 .map(String::from);
286
287 let versions = PackageVersions::from_tags(None, &tags);
288
289 assert_eq!(
290 versions.stable().unwrap(),
291 StableVersion {
292 major: 1,
293 minor: 2,
294 patch: 3
295 }
296 );
297
298 assert_eq!(versions.latest(), Version::from_str("2.0.0-alpha.0").ok());
299 assert_eq!(
300 *versions
301 .prereleases
302 .get(&StableVersion {
303 major: 1,
304 minor: 3,
305 patch: 0
306 })
307 .unwrap()
308 .get(&"alpha".into())
309 .unwrap(),
310 Prerelease::new("alpha".into(), 1)
311 );
312
313 assert_eq!(
314 *versions
315 .prereleases
316 .get(&StableVersion {
317 major: 1,
318 minor: 3,
319 patch: 0
320 })
321 .unwrap()
322 .get(&"beta".into())
323 .unwrap(),
324 Prerelease::new("beta".into(), 0)
325 );
326 }
327}
328
329#[cfg(test)]
330mod test_calculate_new_version {
331 use std::str::FromStr;
332
333 use super::*;
334 use crate::semver::{Rule::*, StableRule::*};
335
336 #[test]
337 fn major() {
338 let versions: PackageVersions = Version::new(1, 2, 3, None).into();
339 let new = versions.calculate_new_version(Stable(Major)).unwrap();
340
341 assert_eq!(new, Version::new(2, 0, 0, None));
342 }
343
344 #[test]
345 fn major_0() {
346 let versions: PackageVersions = Version::new(0, 1, 2, None).into();
347 let new = versions.calculate_new_version(Stable(Major)).unwrap();
348
349 assert_eq!(new, Version::new(0, 2, 0, None));
350 }
351
352 #[test]
353 fn major_pre_only() {
354 let versions = PackageVersions::from_tags(None, &["v1.0.0-rc.0"]);
355 let new = versions.calculate_new_version(Stable(Major)).unwrap();
356
357 assert_eq!(new, Version::new(1, 0, 0, None));
358 }
359
360 #[test]
361 fn major_unset() {
362 let versions = PackageVersions::default();
363 let new = versions.calculate_new_version(Stable(Major)).unwrap();
364
365 assert_eq!(new, Version::new(0, 0, 0, None));
366 }
367
368 #[test]
369 fn major_after_pre() {
370 for pre_version in ["1.2.4-rc.0", "1.3.0-rc.0", "2.0.0-rc.0"] {
371 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
372 versions.update_version(Version::from_str(pre_version).unwrap());
373 let new = versions.calculate_new_version(Stable(Major)).unwrap();
374
375 assert_eq!(new, Version::new(2, 0, 0, None));
376 }
377 }
378
379 #[test]
380 fn minor() {
381 let versions: PackageVersions = Version::new(1, 2, 3, None).into();
382 let new = versions.calculate_new_version(Stable(Minor)).unwrap();
383
384 assert_eq!(new, Version::new(1, 3, 0, None));
385 }
386
387 #[test]
388 fn minor_0() {
389 let versions = PackageVersions::from(Version::new(0, 1, 2, None));
390 let new = versions.calculate_new_version(Stable(Minor)).unwrap();
391
392 assert_eq!(new, Version::new(0, 1, 3, None));
393 }
394
395 #[test]
396 fn minor_pre_only() {
397 let versions = PackageVersions::from_tags(None, &["v1.0.0-rc.0"]);
398 let new = versions.calculate_new_version(Stable(Minor)).unwrap();
399
400 assert_eq!(new, Version::new(1, 0, 0, None));
401 }
402
403 #[test]
404 fn minor_unset() {
405 let versions = PackageVersions::default();
406 let new = versions.calculate_new_version(Stable(Minor)).unwrap();
407
408 assert_eq!(new, Version::new(0, 0, 0, None));
409 }
410
411 #[test]
412 fn minor_after_pre() {
413 for pre_version in ["1.2.4-rc.0", "1.3.0-rc.0"] {
414 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
415 versions.update_version(Version::from_str(pre_version).unwrap());
416 let new = versions.calculate_new_version(Stable(Minor)).unwrap();
417
418 assert_eq!(new, Version::new(1, 3, 0, None));
419 }
420 }
421
422 #[test]
423 fn patch() {
424 let versions = PackageVersions::from(Version::new(1, 2, 3, None));
425 let new = versions.calculate_new_version(Stable(Patch)).unwrap();
426
427 assert_eq!(new, Version::new(1, 2, 4, None));
428 }
429
430 #[test]
431 fn patch_0() {
432 let versions = PackageVersions::from(Version::new(0, 1, 0, None));
433 let new = versions.calculate_new_version(Stable(Patch)).unwrap();
434
435 assert_eq!(new, Version::new(0, 1, 1, None));
436 }
437
438 #[test]
439 fn patch_pre_only() {
440 let versions = PackageVersions::from_tags(None, &["v1.0.0-rc.0"]);
441 let new = versions.calculate_new_version(Stable(Patch)).unwrap();
442
443 assert_eq!(new, Version::new(1, 0, 0, None));
444 }
445
446 #[test]
447 fn patch_unset() {
448 let versions = PackageVersions::default();
449 let new = versions.calculate_new_version(Stable(Patch)).unwrap();
450
451 assert_eq!(new, Version::new(0, 0, 0, None));
452 }
453
454 #[test]
455 fn patch_after_pre() {
456 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
457 versions.update_version(Version::from_str("1.2.4-rc.0").unwrap());
458 let new = versions.calculate_new_version(Stable(Patch)).unwrap();
459
460 assert_eq!(new, Version::new(1, 2, 4, None));
461 }
462
463 #[test]
464 fn pre() {
465 let versions = PackageVersions::from(Version::new(1, 2, 3, None));
466 let new = versions
467 .calculate_new_version(Pre {
468 label: Label::from("rc"),
469 stable_rule: Minor,
470 })
471 .unwrap();
472
473 assert_eq!(new, Version::from_str("1.3.0-rc.0").unwrap());
474 }
475
476 #[test]
477 fn pre_after_same_pre() {
478 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
479 versions.update_version(Version::from_str("1.3.0-rc.0").unwrap());
480 versions.update_version(Version::from_str("1.2.4-rc.1").unwrap());
481 versions.update_version(Version::from_str("2.0.0-rc.2").unwrap());
482 let new = versions
483 .calculate_new_version(Pre {
484 label: Label::from("rc"),
485 stable_rule: Minor,
486 })
487 .unwrap();
488
489 assert_eq!(new, Version::from_str("1.3.0-rc.1").unwrap());
490 }
491
492 #[test]
493 fn pre_without_stable() {
494 let mut versions = PackageVersions::default();
495 versions.update_version(Version::from_str("1.3.0-rc.0").unwrap());
496 versions.update_version(Version::from_str("1.2.4-rc.1").unwrap());
497 versions.update_version(Version::from_str("2.0.0-rc.2").unwrap());
498 let new = versions
499 .calculate_new_version(Pre {
500 label: Label::from("rc"),
501 stable_rule: Minor,
502 })
503 .unwrap();
504
505 assert_eq!(new, Version::from_str("2.0.0-rc.3").unwrap());
506 }
507
508 #[test]
509 fn pre_after_different_pre_version() {
510 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
511 versions.update_version(Version::from_str("1.2.4-beta.1").unwrap());
512 versions.update_version(Version::from_str("1.2.4-rc.0").unwrap());
513 let new = versions
514 .calculate_new_version(Pre {
515 label: Label::from("beta"),
516 stable_rule: Patch,
517 })
518 .unwrap();
519
520 assert_eq!(new, Version::from_str("1.2.4-beta.2").unwrap());
521 }
522
523 #[test]
524 fn pre_after_different_pre_label() {
525 let mut versions = PackageVersions::from(Version::new(1, 2, 3, None));
526 versions.update_version(Version::from_str("1.3.0-beta.0").unwrap());
527 let new = versions
528 .calculate_new_version(Pre {
529 label: Label::from("rc"),
530 stable_rule: Minor,
531 })
532 .unwrap();
533
534 assert_eq!(new, Version::from_str("1.3.0-rc.0").unwrap());
535 }
536
537 #[test]
538 fn release() {
539 let mut versions = PackageVersions::default();
540 versions.update_version(Version::from_str("1.2.3-rc.0").unwrap());
541 versions.update_version(Version::from_str("1.2.4-rc.1").unwrap());
542 versions.update_version(Version::from_str("2.0.0-rc.2").unwrap());
543
544 let new = versions.calculate_new_version(Release).unwrap();
545
546 assert_eq!(new, Version::new(2, 0, 0, None));
547 }
548}