ib-update 0.1.0

A lightweight library for software update
Documentation
/*!
Git tag/version comparison utilities.
*/
use std::borrow::Cow;

use semver::Version;

/// Strip a leading `v`/`V` commonly prefixed to git tags.
pub(crate) fn strip_v(s: &str) -> Cow<'_, str> {
    if let Some(rest) = s.strip_prefix(|c| c == 'v' || c == 'V') {
        Cow::Borrowed(rest)
    } else {
        Cow::Borrowed(s)
    }
}

/// Compare two version strings.
///
/// Tries [`semver`](crate::semver) parsing first; falls back to lexicographic comparison if
/// either side fails to parse.
///
/// Returns `true` if `latest` is strictly newer than `current`.
pub(crate) fn is_newer(current: &str, latest: &str) -> bool {
    let current_clean = strip_v(current);
    let latest_clean = strip_v(latest);

    // Fast-path: semver comparison
    if let (Ok(cv), Ok(lv)) = (
        Version::parse(&current_clean),
        Version::parse(&latest_clean),
    ) {
        return lv > cv;
    }

    // Fallback: lexicographic
    latest_clean > current_clean
}

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

    #[test]
    fn is_newer_semver() {
        assert!(is_newer("1.0.0", "1.0.1"));
        assert!(is_newer("1.0.0", "1.1.0"));
        assert!(is_newer("1.0.0", "2.0.0"));
        assert!(!is_newer("2.0.0", "1.0.0"));
        assert!(!is_newer("1.0.0", "1.0.0"));
    }

    #[test]
    fn is_newer_with_v_prefix() {
        assert!(is_newer("v1.0.0", "v1.0.1"));
        assert!(is_newer("1.0.0", "v1.0.1"));
        assert!(is_newer("v1.0.0", "1.0.1"));
        assert!(!is_newer("v2.0.0", "v1.0.0"));
    }

    #[test]
    fn is_newer_lexicographic_fallback() {
        // Non-semver strings fall back to lexicographic comparison
        assert!(is_newer("alpha", "beta"));
        assert!(!is_newer("beta", "alpha"));
    }

    #[test]
    fn strip_v_() {
        assert_eq!(strip_v("v1.2.3"), "1.2.3");
        assert_eq!(strip_v("V1.2.3"), "1.2.3");
        assert_eq!(strip_v("1.2.3"), "1.2.3");
    }
}