use crate::consts::KERNEL_PATTERN;
use semver::Version;
use std::cmp::Ordering;
use std::fmt;
use std::path::{Path, PathBuf};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VersionEntry {
pub path: PathBuf,
pub version_string: String,
pub semver: Version,
}
impl VersionEntry {
#[must_use]
pub fn new(path: PathBuf, version_string: String, semver: Version) -> Self {
Self {
path,
version_string,
semver,
}
}
#[must_use]
pub fn from_path(path: &Path) -> Option<Self> {
let filename = path.file_name()?.to_string_lossy();
let version_string = filename.to_string();
let version_only = version_string.strip_prefix(KERNEL_PATTERN)?;
let semver = Version::parse(version_only).ok()?;
Some(Self {
path: path.to_path_buf(),
version_string,
semver,
})
}
}
impl PartialOrd for VersionEntry {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for VersionEntry {
fn cmp(&self, other: &Self) -> Ordering {
match self.semver.cmp(&other.semver) {
Ordering::Equal => self.version_string.cmp(&other.version_string),
ord => ord,
}
}
}
impl fmt::Display for VersionEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.version_string, self.semver)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_version_entry_from_path_valid() {
let path = PathBuf::from("/usr/src/linux-6.5.2");
let entry = VersionEntry::from_path(&path).unwrap();
assert_eq!(entry.version_string, "linux-6.5.2");
assert_eq!(entry.semver.major, 6);
assert_eq!(entry.semver.minor, 5);
assert_eq!(entry.semver.patch, 2);
}
#[test]
fn test_version_entry_from_path_invalid() {
let path = PathBuf::from("/usr/src/not-a-kernel");
assert!(VersionEntry::from_path(&path).is_none());
}
#[test]
fn test_version_entry_from_path_with_rc() {
let path = PathBuf::from("/usr/src/linux-6.5.0-rc1");
let entry = VersionEntry::from_path(&path).unwrap();
assert_eq!(entry.version_string, "linux-6.5.0-rc1");
assert!(!entry.semver.pre.is_empty());
}
#[test]
fn test_version_sorting() {
let mut versions = [
VersionEntry::from_path(&PathBuf::from("/usr/src/linux-5.15.0")).unwrap(),
VersionEntry::from_path(&PathBuf::from("/usr/src/linux-6.5.2")).unwrap(),
VersionEntry::from_path(&PathBuf::from("/usr/src/linux-6.1.0")).unwrap(),
];
versions.sort();
let sorted: Vec<String> = versions.iter().map(|v| v.version_string.clone()).collect();
assert_eq!(sorted, vec!["linux-5.15.0", "linux-6.1.0", "linux-6.5.2"]);
}
#[test]
fn test_version_sorting_descending() {
let mut versions = [
VersionEntry::from_path(&PathBuf::from("/usr/src/linux-5.15.0")).unwrap(),
VersionEntry::from_path(&PathBuf::from("/usr/src/linux-6.5.2")).unwrap(),
VersionEntry::from_path(&PathBuf::from("/usr/src/linux-6.1.0")).unwrap(),
];
versions.sort();
versions.reverse();
let sorted: Vec<String> = versions.iter().map(|v| v.version_string.clone()).collect();
assert_eq!(sorted, vec!["linux-6.5.2", "linux-6.1.0", "linux-5.15.0"]);
}
#[test]
fn test_version_entry_display() {
let entry = VersionEntry::from_path(&PathBuf::from("/usr/src/linux-6.5.2")).unwrap();
assert_eq!(entry.to_string(), "linux-6.5.2 (6.5.2)");
}
}