Skip to main content

rabbitmq_versioning/
prerelease.rs

1// Copyright (c) 2025-2026 Michael S. Klishin and Contributors
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::cmp::Ordering;
10use std::fmt;
11
12use serde::{Deserialize, Serialize};
13
14use crate::errors::Error;
15
16#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
17pub enum Prerelease {
18    Alpha(String),
19    Beta(String),
20    Rc(String),
21}
22
23impl Prerelease {
24    pub fn alpha(identifier: impl Into<String>) -> Self {
25        Prerelease::Alpha(identifier.into())
26    }
27
28    pub fn beta(identifier: impl Into<String>) -> Self {
29        Prerelease::Beta(identifier.into())
30    }
31
32    pub fn rc(identifier: impl Into<String>) -> Self {
33        Prerelease::Rc(identifier.into())
34    }
35
36    pub fn is_alpha(&self) -> bool {
37        matches!(self, Prerelease::Alpha(_))
38    }
39
40    pub fn is_beta(&self) -> bool {
41        matches!(self, Prerelease::Beta(_))
42    }
43
44    pub fn is_rc(&self) -> bool {
45        matches!(self, Prerelease::Rc(_))
46    }
47
48    pub fn identifier(&self) -> &str {
49        match self {
50            Prerelease::Alpha(s) | Prerelease::Beta(s) | Prerelease::Rc(s) => s,
51        }
52    }
53
54    pub(crate) fn parse(s: &str, full_version: &str) -> Result<Self, Error> {
55        let parts: Vec<&str> = s.split('.').collect();
56        if parts.len() != 2 {
57            return Err(Error::InvalidVersion(full_version.to_string()));
58        }
59
60        let identifier = parts[1];
61        if identifier.is_empty() {
62            return Err(Error::InvalidVersion(full_version.to_string()));
63        }
64
65        match parts[0].to_lowercase().as_str() {
66            "alpha" => Ok(Prerelease::Alpha(identifier.to_string())),
67            "beta" => Ok(Prerelease::Beta(identifier.to_string())),
68            "rc" => Ok(Prerelease::Rc(identifier.to_string())),
69            _ => Err(Error::InvalidVersion(full_version.to_string())),
70        }
71    }
72}
73
74impl fmt::Display for Prerelease {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        match self {
77            Prerelease::Alpha(s) => write!(f, "alpha.{}", s),
78            Prerelease::Beta(s) => write!(f, "beta.{}", s),
79            Prerelease::Rc(s) => write!(f, "rc.{}", s),
80        }
81    }
82}
83
84fn compare_prerelease_identifiers(a: &str, b: &str) -> Ordering {
85    match (a.parse::<u32>(), b.parse::<u32>()) {
86        (Ok(na), Ok(nb)) => na.cmp(&nb),
87        _ => a.cmp(b),
88    }
89}
90
91impl Ord for Prerelease {
92    fn cmp(&self, other: &Self) -> Ordering {
93        match (self, other) {
94            (Prerelease::Alpha(a), Prerelease::Alpha(b)) => compare_prerelease_identifiers(a, b),
95            (Prerelease::Alpha(_), _) => Ordering::Less,
96            (_, Prerelease::Alpha(_)) => Ordering::Greater,
97            (Prerelease::Beta(a), Prerelease::Beta(b)) => compare_prerelease_identifiers(a, b),
98            (Prerelease::Beta(_), Prerelease::Rc(_)) => Ordering::Less,
99            (Prerelease::Rc(_), Prerelease::Beta(_)) => Ordering::Greater,
100            (Prerelease::Rc(a), Prerelease::Rc(b)) => compare_prerelease_identifiers(a, b),
101        }
102    }
103}
104
105impl PartialOrd for Prerelease {
106    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
107        Some(self.cmp(other))
108    }
109}