Skip to main content

vla_lang/
version.rs

1//! VLA.bas's version predicates (CO.4): `VlaVersionParse`, `VlaVersionCompare`
2//! and `VlaVersionAtLeast`, which a phrasebook's `(requires-version "...")`
3//! is checked with. The version compared against is this crate's, which
4//! `tools/check_version_twin.ps1` holds equal to `VLA_RELEASE_VERSION`.
5
6use std::cmp::Ordering;
7
8use crate::messages::{raise, Refusal};
9
10/// VBA `VlaVersionParse`: `MAJOR.MINOR.PATCH`, each part one to nine
11/// digits, spaces around the whole allowed; `None` for anything else.
12pub fn parse(s: &str) -> Option<(u32, u32, u32)> {
13    let t = s.trim_matches(' ');
14    if t.is_empty() {
15        return None;
16    }
17    let parts: Vec<&str> = t.split('.').collect();
18    if parts.len() != 3 {
19        return None;
20    }
21    let mut n = [0u32; 3];
22    for (i, p) in parts.iter().enumerate() {
23        if p.is_empty() || p.len() > 9 || !p.chars().all(|c| c.is_ascii_digit()) {
24            return None;
25        }
26        n[i] = p.parse().ok()?;
27    }
28    Some((n[0], n[1], n[2]))
29}
30
31/// VBA `VlaVersionCompare`: the order of two versions, refusing a
32/// malformed one by name.
33pub fn compare(a: &str, b: &str) -> Result<Ordering, Refusal> {
34    let pa = parse(a).ok_or_else(|| raise("vla-version-malformed", &[("value", a)]))?;
35    let pb = parse(b).ok_or_else(|| raise("vla-version-malformed", &[("value", b)]))?;
36    Ok(pa.cmp(&pb))
37}
38
39/// VBA `VlaVersionAtLeast`: is this build's version at least `minimum`?
40pub fn at_least(minimum: &str) -> Result<bool, Refusal> {
41    Ok(compare(crate::VERSION, minimum)? != Ordering::Less)
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn parse_takes_three_numeric_parts_and_nothing_else() {
50        assert_eq!(parse("0.5.2"), Some((0, 5, 2)));
51        assert_eq!(parse(" 10.0.1 "), Some((10, 0, 1)));
52        assert_eq!(parse("banana"), None);
53        assert_eq!(parse("1.2"), None);
54        assert_eq!(parse("1.2.3.4"), None);
55        assert_eq!(parse("1..3"), None);
56        assert_eq!(parse("1.2.x"), None);
57        assert_eq!(parse(""), None);
58        assert_eq!(parse("1234567890.0.0"), None);
59    }
60
61    #[test]
62    fn compare_orders_by_part_and_refuses_a_malformed_one() {
63        assert_eq!(compare("0.5.2", "0.5.10").unwrap(), Ordering::Less);
64        assert_eq!(compare("1.0.0", "0.9.9").unwrap(), Ordering::Greater);
65        assert_eq!(compare("0.7.1", "0.7.1").unwrap(), Ordering::Equal);
66        let r = compare("x", "0.1.0").unwrap_err();
67        assert_eq!(r.id, "vla-version-malformed");
68        assert!(at_least("0.0.0").unwrap());
69        assert!(!at_least("999.0.0").unwrap());
70        assert!(at_least(crate::VERSION).unwrap());
71    }
72}