use crate::cargo::manifest_analyzer::DepUsage;
use std::collections::HashSet;
pub fn is_exact_pin(version: &str) -> bool {
version.starts_with('=') && !version.starts_with(">=")
}
pub fn extract_major_version(version: &str) -> Option<u32> {
let cleaned = strip_version_op(version);
let parts: Vec<&str> = cleaned.split('.').collect();
parts.first().and_then(|s| s.parse().ok())
}
pub fn find_major_version_conflicts(usages: &[&DepUsage]) -> HashSet<u32> {
usages
.iter()
.filter_map(|u| u.declared_version.as_ref())
.filter_map(|v| extract_major_version(v))
.collect()
}
pub fn versions_compatible(member_version: &str, workspace_version: &str) -> bool {
let member_ver = strip_version_op(member_version);
let workspace_ver = strip_version_op(workspace_version);
let member_parts: Vec<&str> = member_ver.split('.').collect();
let workspace_parts: Vec<&str> = workspace_ver.split('.').collect();
if member_parts.is_empty() || workspace_parts.is_empty() {
return true; }
let member_major = member_parts[0].parse::<u32>().unwrap_or(0);
let workspace_major = workspace_parts[0].parse::<u32>().unwrap_or(0);
if member_major != workspace_major {
return false;
}
if member_major == 0 {
let member_minor = member_parts.get(1).and_then(|s| s.parse::<u32>().ok()).unwrap_or(0);
let workspace_minor = workspace_parts.get(1).and_then(|s| s.parse::<u32>().ok()).unwrap_or(0);
if member_minor != workspace_minor {
return false;
}
}
true
}
pub fn strip_version_op(version: &str) -> &str {
version
.trim_start_matches(">=")
.trim_start_matches("<=")
.trim_start_matches('>')
.trim_start_matches('<')
.trim_start_matches('=')
.trim_start_matches('^')
.trim_start_matches('~')
.trim()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_extract_major_version() {
assert_eq!(extract_major_version("1.0"), Some(1));
assert_eq!(extract_major_version("2.3.4"), Some(2));
assert_eq!(extract_major_version("0.99.3"), Some(0));
assert_eq!(extract_major_version("^1.0"), Some(1));
assert_eq!(extract_major_version("~2.0"), Some(2));
assert_eq!(extract_major_version(">=3.0"), Some(3));
assert_eq!(extract_major_version("=4.0.0"), Some(4));
assert_eq!(extract_major_version(""), None);
assert_eq!(extract_major_version("invalid"), None);
}
#[test]
fn test_is_exact_pin() {
assert!(is_exact_pin("=1.0.0"));
assert!(is_exact_pin("=0.8.0"));
assert!(!is_exact_pin(">=1.0.0"));
assert!(!is_exact_pin("^1.0.0"));
assert!(!is_exact_pin("1.0.0"));
assert!(!is_exact_pin("~1.0.0"));
}
#[test]
fn test_strip_version_op() {
assert_eq!(strip_version_op("=1.0.0"), "1.0.0");
assert_eq!(strip_version_op("^1.0.0"), "1.0.0");
assert_eq!(strip_version_op(">=1.0.0"), "1.0.0");
assert_eq!(strip_version_op("~1.0.0"), "1.0.0");
assert_eq!(strip_version_op("1.0.0"), "1.0.0");
assert_eq!(strip_version_op("<=2.0"), "2.0");
assert_eq!(strip_version_op(">0.5"), "0.5");
}
#[test]
fn test_versions_compatible_major_match() {
assert!(versions_compatible("1.0", "1.5"));
assert!(versions_compatible("^1.0", "1.5"));
assert!(versions_compatible("2.0", "2.3.1"));
}
#[test]
fn test_versions_compatible_major_mismatch() {
assert!(!versions_compatible("1.0", "2.0"));
assert!(!versions_compatible("^2.0", "3.0"));
}
#[test]
fn test_versions_compatible_zero_major() {
assert!(versions_compatible("0.11", "0.11.5"));
assert!(versions_compatible("^0.11", "0.11"));
assert!(!versions_compatible("0.11", "0.13"));
assert!(!versions_compatible("^0.8", "0.9"));
}
#[test]
fn test_versions_compatible_exact_pins() {
assert!(versions_compatible("=1.0.0", "1.0.0"));
assert!(versions_compatible("=0.8.0", "0.8"));
assert!(!versions_compatible("=1.0.0", "2.0.0"));
}
}