Skip to main content

changepacks_utils/
split_version.rs

1use anyhow::Result;
2
3/// Split a version string into prefix and version parts
4///
5/// # Errors
6/// Returns error if splitting the version fails (currently never fails).
7pub fn split_version(version: &str) -> Result<(Option<String>, String)> {
8    let first_digit_pos = version
9        .char_indices()
10        .find(|(_, c)| c.is_ascii_digit())
11        .map(|(pos, _)| pos);
12
13    match first_digit_pos {
14        Some(0) | None => Ok((None, version.to_string())),
15        Some(pos) => {
16            let prefix = version[..pos].to_string();
17            let version_part = version[pos..].to_string();
18            Ok((Some(prefix), version_part))
19        }
20    }
21}
22
23#[cfg(test)]
24mod tests {
25    use super::*;
26
27    use rstest::rstest;
28
29    #[rstest]
30    #[case("1.0.0", (None, "1.0.0"))]
31    #[case("^1.0.0", (Some("^"), "1.0.0"))]
32    #[case("~1.0.0", (Some("~"), "1.0.0"))]
33    #[case("1.0.0-alpha.1", (None, "1.0.0-alpha.1"))]
34    #[case("1.0.0-alpha.1+build1", (None, "1.0.0-alpha.1+build1"))]
35    #[case(">=1.0.0+build1", (Some(">="), "1.0.0+build1"))]
36    #[case("helloworld-1.0.2", (Some("helloworld-"), "1.0.2"))]
37    #[case("latest", (None, "latest"))]
38    #[case("*", (None, "*"))]
39    fn test_split_version(#[case] input: &str, #[case] expected: (Option<&str>, &str)) {
40        let (prefix, version) = split_version(input).unwrap();
41        assert_eq!(prefix.as_deref(), expected.0);
42        assert_eq!(version.as_str(), expected.1);
43    }
44}