收录版本号比较方法
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
//! ## Modules
|
//! ## Modules
|
||||||
//!
|
//!
|
||||||
//! - [`websocket`] - WebSocket client and server implementations
|
//! - [`websocket`] - WebSocket client and server implementations
|
||||||
|
//! - [`version`] - Version string comparison utilities
|
||||||
//!
|
//!
|
||||||
//! ## Quick Start
|
//! ## Quick Start
|
||||||
//!
|
//!
|
||||||
@@ -27,7 +28,19 @@
|
|||||||
//! client.connect().await;
|
//! client.connect().await;
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Version Comparison
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use cube_lib::version::{version_less_than, needs_update};
|
||||||
|
//!
|
||||||
|
//! // Check if update is needed
|
||||||
|
//! assert!(version_less_than("1.0.0", "1.0.1"));
|
||||||
|
//! assert!(needs_update("1.0.0", "1.0.1"));
|
||||||
|
//! ```
|
||||||
|
|
||||||
pub mod websocket;
|
pub mod websocket;
|
||||||
|
pub mod version;
|
||||||
|
|
||||||
pub use websocket::{WebSocketClient, WebSocketConfig, WebSocketMessage, OutgoingMessage, ConnectionStatus, ReconnectedCallback, MessageSender};
|
pub use websocket::{WebSocketClient, WebSocketConfig, WebSocketMessage, OutgoingMessage, ConnectionStatus, ReconnectedCallback, MessageSender};
|
||||||
|
pub use version::{version_less_than, compare_versions, needs_update, parse_version};
|
||||||
|
|||||||
139
Windows/Rust/CubeLib/src/version.rs
Normal file
139
Windows/Rust/CubeLib/src/version.rs
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
//! # Version utilities
|
||||||
|
//!
|
||||||
|
//! Provides version string comparison utilities for semver-like version strings.
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
/// Parse a version string into a vector of unsigned integers.
|
||||||
|
///
|
||||||
|
/// "1.2.3" → [1, 2, 3]
|
||||||
|
/// "1.2" → [1, 2]
|
||||||
|
/// "1.2.3.4.5" → [1, 2, 3, 4, 5]
|
||||||
|
/// "" or invalid parts are skipped.
|
||||||
|
pub fn parse_version(version: &str) -> Vec<u32> {
|
||||||
|
version
|
||||||
|
.split('.')
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.filter_map(|s| s.parse().ok())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare two version strings.
|
||||||
|
///
|
||||||
|
/// Returns `true` if `current < required` (i.e. current needs to be updated).
|
||||||
|
///
|
||||||
|
/// Comparison is done part-by-part (split by `.`).
|
||||||
|
/// Missing parts are treated as `0`.
|
||||||
|
/// Examples:
|
||||||
|
/// - `"1.0.0"` < `"1.0.1"` → true
|
||||||
|
/// - `"1.0.0"` < `"1.1.0"` → true
|
||||||
|
/// - `"1.0.0"` < `"2.0.0"` → true
|
||||||
|
/// - `"1.0.0"` < `"1.0.0"` → false (equal)
|
||||||
|
/// - `"1.0.1"` < `"1.0.0"` → false
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `current` - The current/local version string
|
||||||
|
/// * `required` - The required/server version string
|
||||||
|
pub fn version_less_than(current: &str, required: &str) -> bool {
|
||||||
|
compare_versions(current, required) == Ordering::Less
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare two version strings, returning the standard `Ordering` result.
|
||||||
|
///
|
||||||
|
/// This is the canonical comparison function. Use [`version_less_than`] if you
|
||||||
|
/// only need a boolean upgrade check.
|
||||||
|
pub fn compare_versions(a: &str, b: &str) -> Ordering {
|
||||||
|
let a_parts = parse_version(a);
|
||||||
|
let b_parts = parse_version(b);
|
||||||
|
|
||||||
|
let max_len = a_parts.len().max(b_parts.len());
|
||||||
|
|
||||||
|
for i in 0..max_len {
|
||||||
|
let av = a_parts.get(i).copied().unwrap_or(0);
|
||||||
|
let bv = b_parts.get(i).copied().unwrap_or(0);
|
||||||
|
|
||||||
|
match av.cmp(&bv) {
|
||||||
|
Ordering::Less => return Ordering::Less,
|
||||||
|
Ordering::Greater => return Ordering::Greater,
|
||||||
|
Ordering::Equal => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ordering::Equal
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a local version is outdated compared to a server version.
|
||||||
|
///
|
||||||
|
/// Returns `true` if:
|
||||||
|
/// - local is "0.0.0" / empty (file does not exist)
|
||||||
|
/// - local version < server version
|
||||||
|
pub fn needs_update(local_version: &str, server_version: &str) -> bool {
|
||||||
|
local_version == "0.0.0"
|
||||||
|
|| local_version.is_empty()
|
||||||
|
|| version_less_than(local_version, server_version)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_version() {
|
||||||
|
assert_eq!(parse_version("1.2.3"), vec![1, 2, 3]);
|
||||||
|
assert_eq!(parse_version("1.2"), vec![1, 2]);
|
||||||
|
assert_eq!(parse_version("1.2.3.4.5"), vec![1, 2, 3, 4, 5]);
|
||||||
|
assert_eq!(parse_version(""), Vec::<u32>::new());
|
||||||
|
assert_eq!(parse_version("1..3"), vec![1, 3]);
|
||||||
|
assert_eq!(parse_version("abc"), Vec::<u32>::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_version_less_than() {
|
||||||
|
// Basic comparisons
|
||||||
|
assert!(version_less_than("1.0.0", "1.0.1"));
|
||||||
|
assert!(version_less_than("1.0.0", "1.1.0"));
|
||||||
|
assert!(version_less_than("1.0.0", "2.0.0"));
|
||||||
|
assert!(version_less_than("0.0.1", "0.0.2"));
|
||||||
|
|
||||||
|
// Equal versions
|
||||||
|
assert!(!version_less_than("1.0.0", "1.0.0"));
|
||||||
|
assert!(!version_less_than("1.2.3", "1.2.3"));
|
||||||
|
|
||||||
|
// Newer local version
|
||||||
|
assert!(!version_less_than("1.0.1", "1.0.0"));
|
||||||
|
assert!(!version_less_than("2.0.0", "1.0.0"));
|
||||||
|
|
||||||
|
// Different part counts
|
||||||
|
assert!(version_less_than("1.0", "1.0.1"));
|
||||||
|
assert!(version_less_than("1", "1.0.1"));
|
||||||
|
assert!(version_less_than("1.0.0", "1.0.0.1"));
|
||||||
|
assert!(version_less_than("1", "2"));
|
||||||
|
|
||||||
|
// Treats missing parts as 0
|
||||||
|
assert!(version_less_than("1", "1.0.1"));
|
||||||
|
assert!(!version_less_than("1.0.0", "1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compare_versions() {
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
assert_eq!(compare_versions("1.0.0", "1.0.1"), Ordering::Less);
|
||||||
|
assert_eq!(compare_versions("1.0.1", "1.0.0"), Ordering::Greater);
|
||||||
|
assert_eq!(compare_versions("1.0.0", "1.0.0"), Ordering::Equal);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_needs_update() {
|
||||||
|
// Missing local
|
||||||
|
assert!(needs_update("0.0.0", "1.0.0"));
|
||||||
|
assert!(needs_update("", "1.0.0"));
|
||||||
|
|
||||||
|
// Needs update
|
||||||
|
assert!(needs_update("1.0.0", "1.0.1"));
|
||||||
|
assert!(needs_update("1.0.0", "2.0.0"));
|
||||||
|
|
||||||
|
// Already up to date
|
||||||
|
assert!(!needs_update("1.0.0", "1.0.0"));
|
||||||
|
assert!(!needs_update("1.0.1", "1.0.0"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user