Skip to main content

kernel_builder/
discovery.rs

1use crate::consts::KERNEL_PATTERN;
2use semver::Version;
3use std::cmp::Ordering;
4use std::fmt;
5use std::path::{Path, PathBuf};
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct VersionEntry {
9    pub path: PathBuf,
10    pub version_string: String,
11    pub semver: Version,
12}
13
14impl VersionEntry {
15    #[must_use]
16    pub fn new(path: PathBuf, version_string: String, semver: Version) -> Self {
17        Self {
18            path,
19            version_string,
20            semver,
21        }
22    }
23
24    #[must_use]
25    pub fn from_path(path: &Path) -> Option<Self> {
26        let filename = path.file_name()?.to_string_lossy();
27        let version_string = filename.to_string();
28        let version_only = version_string.strip_prefix(KERNEL_PATTERN)?;
29        let semver = Version::parse(version_only).ok()?;
30        Some(Self {
31            path: path.to_path_buf(),
32            version_string,
33            semver,
34        })
35    }
36}
37
38impl PartialOrd for VersionEntry {
39    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
40        Some(self.cmp(other))
41    }
42}
43
44impl Ord for VersionEntry {
45    fn cmp(&self, other: &Self) -> Ordering {
46        match self.semver.cmp(&other.semver) {
47            Ordering::Equal => self.version_string.cmp(&other.version_string),
48            ord => ord,
49        }
50    }
51}
52
53impl fmt::Display for VersionEntry {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        write!(f, "{} ({})", self.version_string, self.semver)
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn test_version_entry_from_path_valid() {
65        let path = PathBuf::from("/usr/src/linux-6.5.2");
66        let entry = VersionEntry::from_path(&path).unwrap();
67        assert_eq!(entry.version_string, "linux-6.5.2");
68        assert_eq!(entry.semver.major, 6);
69        assert_eq!(entry.semver.minor, 5);
70        assert_eq!(entry.semver.patch, 2);
71    }
72
73    #[test]
74    fn test_version_entry_from_path_invalid() {
75        let path = PathBuf::from("/usr/src/not-a-kernel");
76        assert!(VersionEntry::from_path(&path).is_none());
77    }
78
79    #[test]
80    fn test_version_entry_from_path_with_rc() {
81        let path = PathBuf::from("/usr/src/linux-6.5.0-rc1");
82        let entry = VersionEntry::from_path(&path).unwrap();
83        assert_eq!(entry.version_string, "linux-6.5.0-rc1");
84        assert!(!entry.semver.pre.is_empty());
85    }
86
87    #[test]
88    fn test_version_sorting() {
89        let mut versions = [
90            VersionEntry::from_path(&PathBuf::from("/usr/src/linux-5.15.0")).unwrap(),
91            VersionEntry::from_path(&PathBuf::from("/usr/src/linux-6.5.2")).unwrap(),
92            VersionEntry::from_path(&PathBuf::from("/usr/src/linux-6.1.0")).unwrap(),
93        ];
94
95        versions.sort();
96        let sorted: Vec<String> = versions.iter().map(|v| v.version_string.clone()).collect();
97        assert_eq!(sorted, vec!["linux-5.15.0", "linux-6.1.0", "linux-6.5.2"]);
98    }
99
100    #[test]
101    fn test_version_sorting_descending() {
102        let mut versions = [
103            VersionEntry::from_path(&PathBuf::from("/usr/src/linux-5.15.0")).unwrap(),
104            VersionEntry::from_path(&PathBuf::from("/usr/src/linux-6.5.2")).unwrap(),
105            VersionEntry::from_path(&PathBuf::from("/usr/src/linux-6.1.0")).unwrap(),
106        ];
107
108        versions.sort();
109        versions.reverse();
110        let sorted: Vec<String> = versions.iter().map(|v| v.version_string.clone()).collect();
111        assert_eq!(sorted, vec!["linux-6.5.2", "linux-6.1.0", "linux-5.15.0"]);
112    }
113
114    #[test]
115    fn test_version_entry_display() {
116        let entry = VersionEntry::from_path(&PathBuf::from("/usr/src/linux-6.5.2")).unwrap();
117        assert_eq!(entry.to_string(), "linux-6.5.2 (6.5.2)");
118    }
119}