openlatch-provider 0.2.1

Self-service onboarding CLI + runtime daemon for OpenLatch Editors and Providers
//! SemVer auto-bump for `publish [bump]`. Implementation in P1.T7.

use crate::error::{OlError, OL_4210_SCHEMA_MISMATCH};

#[derive(Debug, Clone, Copy)]
pub enum Bump {
    Major,
    Minor,
    Patch,
}

/// Apply a SemVer bump to the given version string. Returns the new version
/// string. Rejects pre-release / build-metadata suffixes for now.
pub fn bump(version: &str, bump: Bump) -> Result<String, OlError> {
    let mut parts = version.split('.');
    let major: u64 = parts
        .next()
        .and_then(|s| s.parse().ok())
        .ok_or_else(|| OlError::new(OL_4210_SCHEMA_MISMATCH, format!("not semver: `{version}`")))?;
    let minor: u64 = parts
        .next()
        .and_then(|s| s.parse().ok())
        .ok_or_else(|| OlError::new(OL_4210_SCHEMA_MISMATCH, format!("not semver: `{version}`")))?;
    let patch_part = parts
        .next()
        .ok_or_else(|| OlError::new(OL_4210_SCHEMA_MISMATCH, format!("not semver: `{version}`")))?;
    if parts.next().is_some() {
        return Err(OlError::new(
            OL_4210_SCHEMA_MISMATCH,
            format!("not semver (extra components): `{version}`"),
        ));
    }
    let patch: u64 = patch_part.parse().map_err(|_| {
        OlError::new(
            OL_4210_SCHEMA_MISMATCH,
            format!("not semver (patch): `{version}`"),
        )
    })?;
    Ok(match bump {
        Bump::Major => format!("{}.0.0", major + 1),
        Bump::Minor => format!("{}.{}.0", major, minor + 1),
        Bump::Patch => format!("{}.{}.{}", major, minor, patch + 1),
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn bump_minor() {
        assert_eq!(bump("1.2.3", Bump::Minor).unwrap(), "1.3.0");
    }

    #[test]
    fn bump_major() {
        assert_eq!(bump("1.2.3", Bump::Major).unwrap(), "2.0.0");
    }

    #[test]
    fn bump_patch() {
        assert_eq!(bump("1.2.3", Bump::Patch).unwrap(), "1.2.4");
    }

    #[test]
    fn rejects_non_semver() {
        assert!(bump("1.2", Bump::Patch).is_err());
        assert!(bump("1.2.3.4", Bump::Patch).is_err());
        assert!(bump("v1.2.3", Bump::Patch).is_err());
    }
}