CH-J Toolsserver management over SSH
Menu
Published source

CH-J Tools

Browse directories and files for a specific application release.

Download source ZIP
CH-J / Server Manager 1704 files
0.1.2-beta Added: · Built:
version.rs Added: Date not recorded 2.9 KB · 77 lines
Code Download file
1//! Version parsing/comparison (port of `shared/version.js`): 2–4 numeric components,
2//! optional prerelease and build metadata.
4use crate::core::error::{CoreError, CoreResult};
5use once_cell::sync::Lazy;
6use regex::Regex;
7use std::cmp::Ordering;
9static VERSION: Lazy<Regex> = Lazy::new(|| {
10 Regex::new(r"^v?(\d+)\.(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$").unwrap()
11});
13#[derive(Debug, Clone, PartialEq)]
14pub struct Version {
15 pub numbers: [u64; 4],
16 pub prerelease: Vec<String>,
19pub fn parse_version(input: &str) -> CoreResult<Version> {
20 let raw = input.trim();
21 let caps = VERSION.captures(raw).ok_or_else(|| CoreError::plain(format!("Invalid version: {}", if raw.is_empty() { "<empty>" } else { raw })))?;
22 let n = |i: usize| caps.get(i).map(|m| m.as_str().parse::<u64>().unwrap_or(u64::MAX)).unwrap_or(0);
23 Ok(Version {
24 numbers: [n(1), n(2), n(3), n(4)],
25 prerelease: caps.get(5).map(|m| m.as_str().split('.').map(str::to_string).collect()).unwrap_or_default(),
26 })
29fn compare_prerelease(left: &[String], right: &[String]) -> Ordering {
30 match (left.is_empty(), right.is_empty()) {
31 (true, true) => return Ordering::Equal,
32 (true, false) => return Ordering::Greater,
33 (false, true) => return Ordering::Less,
34 _ => {}
35 }
36 for i in 0..left.len().max(right.len()) {
37 let (Some(l), Some(r)) = (left.get(i), right.get(i)) else {
38 return if left.get(i).is_none() { Ordering::Less } else { Ordering::Greater };
39 };
40 if l == r {
41 continue;
42 }
43 let ln = l.chars().all(|c| c.is_ascii_digit());
44 let rn = r.chars().all(|c| c.is_ascii_digit());
45 return match (ln, rn) {
46 (true, true) => l.parse::<u128>().unwrap_or(0).cmp(&r.parse::<u128>().unwrap_or(0)),
47 (true, false) => Ordering::Less,
48 (false, true) => Ordering::Greater,
49 _ => l.cmp(r),
50 };
51 }
52 Ordering::Equal
55pub fn compare_versions(left: &str, right: &str) -> CoreResult<Ordering> {
56 let (l, r) = (parse_version(left)?, parse_version(right)?);
57 Ok(l.numbers.cmp(&r.numbers).then_with(|| compare_prerelease(&l.prerelease, &r.prerelease)))
60pub fn cmp_or_equal(left: &str, right: &str) -> Ordering {
61 compare_versions(left, right).unwrap_or(Ordering::Equal)
64#[cfg(test)]
65mod tests {
66 use super::*;
68 #[test]
69 fn ordering() {
70 assert_eq!(compare_versions("0.1.1", "0.1.0").unwrap(), Ordering::Greater);
71 assert_eq!(compare_versions("1.0.0-alpha", "1.0.0").unwrap(), Ordering::Less);
72 assert_eq!(compare_versions("1.0.0-alpha.2", "1.0.0-alpha.10").unwrap(), Ordering::Less);
73 assert_eq!(compare_versions("1.0", "1.0.0.0").unwrap(), Ordering::Equal);
74 assert_eq!(compare_versions("v2.0.0+build", "2.0.0").unwrap(), Ordering::Equal);
75 assert!(parse_version("1").is_err());
76 }
SHA-25621f4c48ca8ccc12cb37617185b5a3818d08529a95731df0e208be2482111f0c9

CH-J Proprietary Software License 1.14 · Source is provided under the CH-J Proprietary Software License 1.14. Its availability does not change the license terms or grant additional rights.

Archive SHA-256ac8fe790f4ba60fc098f8c985fc2656cfcc6cdc21fb34b30e308a2b54e62258f
Files