kernel_builder/
discovery.rs1use 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}