rabbitmq_versioning/
version.rs1use std::cmp::Ordering;
10use std::fmt;
11use std::str::FromStr;
12
13use serde::{Deserialize, Serialize};
14
15use crate::errors::Error;
16use crate::prerelease::Prerelease;
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub struct Version {
20 pub major: u32,
21 pub minor: u32,
22 pub patch: u32,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub prerelease: Option<Prerelease>,
25}
26
27impl Version {
28 pub fn new(major: u32, minor: u32, patch: u32) -> Self {
29 Self {
30 major,
31 minor,
32 patch,
33 prerelease: None,
34 }
35 }
36
37 pub fn with_prerelease(major: u32, minor: u32, patch: u32, prerelease: Prerelease) -> Self {
38 Self {
39 major,
40 minor,
41 patch,
42 prerelease: Some(prerelease),
43 }
44 }
45
46 pub fn dir_name(&self) -> String {
47 self.to_string()
48 }
49
50 pub fn is_ga(&self) -> bool {
51 self.prerelease.is_none()
52 }
53
54 pub fn is_prerelease(&self) -> bool {
55 self.prerelease.is_some()
56 }
57
58 pub fn is_alpha(&self) -> bool {
59 self.prerelease.as_ref().is_some_and(|p| p.is_alpha())
60 }
61
62 pub fn is_beta(&self) -> bool {
63 self.prerelease.as_ref().is_some_and(|p| p.is_beta())
64 }
65
66 pub fn is_rc(&self) -> bool {
67 self.prerelease.as_ref().is_some_and(|p| p.is_rc())
68 }
69
70 pub fn is_distributed_via_server_packages_repository(&self) -> bool {
71 self.is_alpha()
72 }
73
74 pub fn download_url(&self) -> String {
75 format!(
76 "https://github.com/rabbitmq/rabbitmq-server/releases/download/v{v}/rabbitmq-server-generic-unix-{v}.tar.xz",
77 v = self
78 )
79 }
80
81 pub fn download_url_with_tag(&self, tag: &str) -> String {
82 format!(
83 "https://github.com/rabbitmq/server-packages/releases/download/{tag}/rabbitmq-server-generic-unix-{v}.tar.xz",
84 tag = tag,
85 v = self
86 )
87 }
88
89 pub fn archive_name(&self) -> String {
90 format!("rabbitmq-server-generic-unix-{}.tar.xz", self)
91 }
92
93 pub fn extracted_dir_name(&self) -> String {
94 format!("rabbitmq_server-{}", self)
95 }
96
97 pub fn base_version(&self) -> Version {
98 Version::new(self.major, self.minor, self.patch)
99 }
100}
101
102impl fmt::Display for Version {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?;
105 if let Some(ref pre) = self.prerelease {
106 write!(f, "-{}", pre)?;
107 }
108 Ok(())
109 }
110}
111
112impl FromStr for Version {
113 type Err = Error;
114
115 fn from_str(s: &str) -> Result<Self, Self::Err> {
116 let s = s.trim().trim_start_matches('v');
117
118 let (version_part, prerelease) = if let Some(idx) = s.find('-') {
119 let (ver, pre) = s.split_at(idx);
120 let pre = &pre[1..];
121 (ver, Some(Prerelease::parse(pre, s)?))
122 } else {
123 (s, None)
124 };
125
126 let parts: Vec<&str> = version_part.split('.').collect();
127
128 if parts.len() != 3 {
129 return Err(Error::InvalidVersion(s.to_string()));
130 }
131
132 let major = parts[0]
133 .parse()
134 .map_err(|_| Error::InvalidVersion(s.to_string()))?;
135 let minor = parts[1]
136 .parse()
137 .map_err(|_| Error::InvalidVersion(s.to_string()))?;
138 let patch = parts[2]
139 .parse()
140 .map_err(|_| Error::InvalidVersion(s.to_string()))?;
141
142 Ok(Version {
143 major,
144 minor,
145 patch,
146 prerelease,
147 })
148 }
149}
150
151impl Ord for Version {
152 fn cmp(&self, other: &Self) -> Ordering {
153 let base =
154 (self.major, self.minor, self.patch).cmp(&(other.major, other.minor, other.patch));
155 if base != Ordering::Equal {
156 return base;
157 }
158
159 match (&self.prerelease, &other.prerelease) {
160 (None, None) => Ordering::Equal,
161 (Some(_), None) => Ordering::Less,
162 (None, Some(_)) => Ordering::Greater,
163 (Some(a), Some(b)) => a.cmp(b),
164 }
165 }
166}
167
168impl PartialOrd for Version {
169 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
170 Some(self.cmp(other))
171 }
172}