cargo_uv/version.rs
1use miette::{IntoDiagnostic, LabeledSpan, bail, ensure, miette};
2use semver::{BuildMetadata, Prerelease, Version};
3use std::str::FromStr;
4use tracing::instrument;
5
6use crate::{Action, Result, current_span, error::VersionError};
7pub trait Bumpable {
8 /// Used to bump the version then set the [`Prerelease`] and [`BuildMetadata`].
9 fn bump(
10 &mut self,
11 action: Action,
12 pre_release: Option<Prerelease>,
13 build: Option<BuildMetadata>,
14 force_version: bool,
15 ) -> Result<Version>;
16
17 fn try_bump_pre(&mut self, force: bool) -> Result<Version>;
18 fn try_bump_patch(&mut self) -> Result<Version>;
19 fn try_bump_minor(&mut self, force: bool) -> Result<Version>;
20 fn try_bump_major(&mut self, force: bool) -> Result<Version>;
21}
22
23impl Bumpable for Version {
24 #[instrument(fields(from, to), skip(self))]
25 fn bump(
26 &mut self,
27 action: Action,
28 pre_release: Option<Prerelease>,
29 build: Option<BuildMetadata>,
30 force_version: bool,
31 ) -> Result<Version> {
32 use crate::cli::Action as BumpVer;
33 let span = current_span!();
34 let old_version = self.clone();
35 span.record("from", self.to_string());
36 tracing::trace!("Bumping version");
37 match action {
38 BumpVer::Patch => self.try_bump_patch()?,
39 BumpVer::Minor => self.try_bump_minor(force_version)?,
40 BumpVer::Major => self.try_bump_major(force_version)?,
41 BumpVer::Pre => self.try_bump_pre(force_version)?,
42 _ => bail!("Invalid Action: {}", action),
43 };
44 if action != Action::Pre {
45 if let Some(pre) = pre_release {
46 self.pre = pre
47 }
48 };
49
50 if let Some(build) = build {
51 self.build = build
52 }
53 if !force_version {
54 ensure!(
55 self.clone() > old_version,
56 "New version is not large than old version"
57 );
58 }
59 let ver_str = &self.clone().to_string();
60 span.record("to", ver_str);
61 tracing::debug!("Version bumped to: {}", ver_str);
62 Ok(self.clone())
63 }
64
65 fn try_bump_patch(&mut self) -> Result<Version> {
66 let old_version = self.clone();
67 let version = self;
68 if version.pre.is_empty() {
69 version.patch += 1;
70 } else {
71 version.pre = semver::Prerelease::EMPTY
72 }
73
74 ensure!(
75 &old_version < version,
76 "Patch bump error: old={}, new={}",
77 old_version.to_string(),
78 version.clone().to_string()
79 );
80 Ok(version.clone())
81 }
82
83 fn try_bump_minor(&mut self, force: bool) -> Result<Version> {
84 let old_version = self.clone();
85 let version = self;
86 if !version.pre.is_empty() && !force {
87 Err(VersionError::prerelease_not_empty(
88 &old_version,
89 Action::Minor,
90 ))?;
91 }
92 version.pre = Prerelease::EMPTY;
93 version.minor += 1;
94 version.patch = 0;
95 ensure!(&old_version < version, "Failed to bump: Minor");
96 Ok(version.clone())
97 }
98
99 fn try_bump_major(&mut self, force: bool) -> Result<Version> {
100 let old_version = self.clone();
101 let version = self;
102 if !version.pre.is_empty() && !force {
103 Err(VersionError::prerelease_not_empty(
104 &old_version,
105 Action::Minor,
106 ))?;
107 }
108 version.pre = Prerelease::EMPTY;
109 version.major += 1;
110 version.minor = 0;
111 version.patch = 0;
112 ensure!(&old_version < version, "Failed to bump: Major");
113 Ok(version.clone())
114 }
115 ///
116 /// Pre-release must be in form `-<ascii>.<number>`
117 /// TODO: What to do when not set. Should we match on a -> b -> rc
118 #[instrument(skip(self), fields(from, to))]
119 fn try_bump_pre(&mut self, force: bool) -> Result<Version> {
120 let span = current_span!();
121 span.record("from", self.to_string());
122 tracing::trace!("Bumping pre");
123 let old_version = self.clone();
124 if self.pre == Prerelease::EMPTY {
125 bail!(
126 help = "Set the pre-release either with the pre-release flag `--pre` or using the `set` action.",
127 "Prerelease not set."
128 );
129 }
130
131 if let Some((id, num)) = self.pre.to_string().split_once('.') {
132 let num = match u64::from_str(num) {
133 Ok(n) => n + 1,
134 Err(e) => {
135 let source_code = self.pre.to_string();
136 let split_idx = source_code.find('.').expect("Already checked");
137 let mut span = (
138 split_idx,
139 source_code.chars().count() as isize - 1isize - split_idx as isize,
140 );
141 if span.1 < 0 {
142 span.1 = 0isize
143 }
144
145 let mut span = (span.0, span.1 as usize);
146 span.0 += self.to_string().find('-').unwrap() + 2 + 11;
147 let labels = vec![LabeledSpan::new(
148 Some("Invalid incrementable segment".into()),
149 span.0,
150 span.1,
151 )];
152 let err = miette!(
153 labels = labels,
154 help = "Must only be a number after the '.' in prerelease segment.",
155 "When converting to int: {e}"
156 )
157 .with_source_code(format!(r#"version = "{}""#, self.to_string()));
158
159 return Err(err);
160 }
161 };
162 let pre = Prerelease::new(&format!("{id}.{num}")).into_diagnostic()?;
163 self.pre = pre
164 } else {
165 bail!("Prerelease not split by '.': {}", self.pre.to_string());
166 };
167
168 ensure!(self.clone() > old_version, "PreRelease bump failed.");
169 span.record("to", self.to_string());
170 tracing::debug!("Prerelease bumped.");
171
172 Ok(self.clone())
173 }
174}
175
176pub trait Setable {
177 fn set_version(&mut self, new_version: Version) -> Result<Version>;
178 fn set_prerelease(&mut self, new_prerelease: Prerelease) -> Result<Version>;
179}
180impl Setable for Version {
181 #[instrument(skip_all, fields(from, to))]
182 fn set_version(&mut self, new_version: Version) -> Result<Version> {
183 let span = current_span!();
184 span.record("from", self.to_string());
185 span.record("to", new_version.to_string());
186 tracing::debug!("Setting version");
187
188 self.build = new_version.build.clone();
189 self.pre = new_version.pre.clone();
190 self.patch = new_version.patch;
191 self.minor = new_version.minor;
192 self.major = new_version.major;
193
194 miette::ensure!(self.clone() == new_version, "Failed to set version");
195 Ok(self.clone())
196 }
197
198 #[instrument(skip_all, fields(from, to))]
199 fn set_prerelease(&mut self, new_prerelease: Prerelease) -> Result<Version> {
200 let span = current_span!();
201 span.record("from", self.to_string());
202 tracing::trace!("set_prerelease");
203 self.pre = new_prerelease;
204 span.record("to", self.to_string());
205 tracing::debug!("Set new prerelease");
206 Ok(self.clone())
207 }
208}
209
210#[allow(dead_code)]
211/// TODO: Implement for any incrimentable type within reason.
212pub trait Incrimentable {
213 fn increment(&mut self);
214 fn increment_by(&mut self, n: isize);
215}
216
217// #[cfg(test)]
218// mod tests {
219// use semver::BuildMetadata;
220// use semver::Prerelease;
221
222// use super::*;
223
224// macro_rules! version {
225// ($maj:literal $min:literal $pat:literal) => {
226// Version::new($maj, $min, $pat)
227// };
228// ($maj:literal $min:literal $pat:literal -$pre:ident) => {{
229// let mut v = version!($maj $min $pat);
230// v.pre = semver::Prerelease::new(stringify!($pre)).unwrap_or_default();
231// v
232// }};
233// ($maj:literal $min:literal $pat:literal -$pre:ident +$build:ident) => {{
234// let mut v = version!($maj $min $pat -$pre);
235// v.build = semver::BuildMetadata::new(stringify!($build)).unwrap_or_default();
236// v
237// }};
238// ($ver:literal) => {
239// semver::Version::parse($ver).unwrap()
240// }
241// }
242// #[test]
243// fn test_version_macro() {
244// let basic = Version::new(1, 1, 0);
245// assert_eq!(basic, version!(1 1 0));
246
247// let mut basic_with_pre = basic.clone();
248// basic_with_pre.pre = semver::Prerelease::new("alpha").unwrap();
249// assert_eq!(basic_with_pre, version!(1 1 0 -alpha));
250// let mut basic_with_pre_and_build = basic_with_pre.clone();
251// basic_with_pre_and_build.build = BuildMetadata::new("test").unwrap();
252// assert_ne!(basic_with_pre, basic_with_pre_and_build);
253// assert_eq!(basic_with_pre_and_build, version!(1 1 0 -alpha +test));
254// }
255
256// #[test]
257// fn bump_patch_simple() {
258// let mut version = version!(0 1 1);
259
260// bump_patch(&mut version);
261// assert_eq!(version, version!(0 1 2), "Bump 0.1.1 -> 0.1.2");
262// bump_patch(&mut version);
263// assert_eq!(version, version!(0 1 3), "Bump 0.1.2 -> 0.1.3");
264// }
265
266// #[test]
267// fn bump_patch_pre() {
268// let mut version = version!("0.1.1-alpha.2");
269
270// assert_eq!(version.pre, Prerelease::new("alpha.2").unwrap());
271// bump_patch(&mut version);
272// assert_eq!(version, version!(0 1 1));
273// assert!(version > version!("0.1.1-alpha.2"));
274// }
275
276// #[test]
277// fn bump_minor_simple() {
278// let mut version = version!(0 1 1);
279
280// try_bump_minor(&mut version, false).unwrap();
281// assert_eq!(version, version!(0 2 0), "Bump 0.1.1 -> 0.2.0");
282// try_bump_minor(&mut version, false).unwrap();
283// assert_eq!(version, version!(0 3 0), "Bump 0.2.0 -> 0.3.0");
284// }
285
286// #[test]
287// fn bump_minor_pre_force() {
288// let mut version = version!("0.1.1-alpha.2");
289
290// assert_eq!(version.pre, Prerelease::new("alpha.2").unwrap());
291// try_bump_minor(&mut version, true).unwrap();
292// assert_eq!(version, version!(0 2 0), "Bump 0.1.1-alpha.2 -> 0.2.0");
293// assert!(version > version!("0.1.1-alpha.2"));
294// }
295// #[test]
296// #[should_panic]
297// fn bump_minor_pre_no_force() {
298// let mut version = version!("0.1.1-alpha.2");
299
300// assert_eq!(version.pre, Prerelease::new("alpha.2").unwrap());
301// try_bump_minor(&mut version, false).unwrap();
302// }
303
304// #[test]
305// fn bump_major_simple() -> miette::Result<()> {
306// let mut version = version!(0 1 1);
307
308// try_bump_major(&mut version, false)?;
309// assert_eq!(version, version!(1 0 0), "Bump 0.1.1 -> 1.0.0");
310// try_bump_major(&mut version, false)?;
311// assert_eq!(version, version!(2 0 0), "Bump 1.0.0 -> 2.0.0");
312// Ok(())
313// }
314
315// #[test]
316// fn bump_major_pre() -> miette::Result<()> {
317// let mut version = version!("0.1.1-alpha.2");
318
319// assert_eq!(version.pre, Prerelease::new("alpha.2").unwrap());
320// try_bump_major(&mut version, true)?;
321// assert_eq!(version, version!(1 0 0), "Bump 0.1.1-alpha.2 -> 1.0.0");
322// assert!(version > version!("0.1.1-alpha.2"));
323// Ok(())
324// }
325
326// #[test]
327// #[should_panic]
328// fn bump_major_pre_no_force() {
329// let mut version = version!("0.1.1-alpha.2");
330
331// assert_eq!(version.pre, Prerelease::new("alpha.2").unwrap());
332// try_bump_major(&mut version, false).unwrap();
333// }
334// }