Skip to main content

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// }