eutils_rs/
kernel_version.rs

1use anyhow::{bail, Result};
2
3fn get_current_kernel_version() -> Result<String> {
4    let mut info = unsafe { std::mem::MaybeUninit::<libc::utsname>::zeroed().assume_init() };
5    let mut release_version = Vec::with_capacity(info.release.len());
6    let ret = unsafe { libc::uname(&mut info as *mut libc::utsname) };
7    if ret < 0 {
8        bail!("failed to call function: libc::uname, error code: {}", ret)
9    }
10
11    for i in info.release {
12        release_version.push(i as u8);
13    }
14
15    Ok(String::from_utf8(release_version)?)
16}
17
18// see: https://doc.rust-lang.org/book/appendix-03-derivable-traits.html?highlight=ord#partialord-and-ord-for-ordering-comparisons
19#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
20pub struct KernelVersion {
21    major_revision: i32,
22    minor_revision: i32,
23    patch_number: i32,
24}
25
26impl TryFrom<&str> for KernelVersion {
27    type Error = anyhow::Error;
28    fn try_from(version_string: &str) -> Result<Self> {
29        let mut start_pos = 0;
30        let mut major_revision = 0;
31        let mut minor_revision = 0;
32        let mut patch_number = 0;
33
34        if let Some(pos) = version_string[start_pos..version_string.len()].find('.') {
35            let part = &version_string[start_pos..pos];
36            major_revision = part.parse()?;
37
38            start_pos = pos + 1;
39        } else {
40            bail!("failed to parse {}", version_string)
41        }
42
43        if let Some(pos) = version_string[start_pos..version_string.len()].find('.') {
44            let part = &version_string[start_pos..start_pos + pos];
45            minor_revision = part.parse()?;
46
47            start_pos = start_pos + pos + 1;
48        } else {
49            bail!("failed to parse {}", version_string)
50        }
51
52        for i in start_pos..version_string.len() + 1 {
53            let mut char = '-' as u8;
54
55            if i < version_string.len() {
56               char =  version_string.as_bytes()[i];
57            }
58            if char < '0' as u8 || char > '9' as u8 {
59                let part = &version_string[start_pos..i];
60                patch_number = part.parse()?;
61                break;
62            }
63        }
64
65        Ok(KernelVersion {
66            major_revision,
67            minor_revision,
68            patch_number,
69        })
70    }
71}
72
73impl KernelVersion {
74    pub fn current() -> Result<KernelVersion> {
75        let str = get_current_kernel_version()?;
76        KernelVersion::try_from(str.as_str())
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    #[test]
84    fn test_get_current_kernel_version() {
85        assert_eq!(get_current_kernel_version().is_ok(), true);
86    }
87
88    #[test]
89    fn test_kernel_version_try_from1() {
90        let kv = KernelVersion::try_from("3.10.10").unwrap();
91        assert_eq!(kv.major_revision, 3);
92        assert_eq!(kv.minor_revision, 10);
93        assert_eq!(kv.patch_number, 10);
94    }
95
96    #[test]
97    fn test_kernel_version_try_from2() {
98        let kv = KernelVersion::try_from("3.10.10-xx").unwrap();
99        assert_eq!(kv.major_revision, 3);
100        assert_eq!(kv.minor_revision, 10);
101        assert_eq!(kv.patch_number, 10);
102    }
103
104    #[test]
105    fn test_kernel_version_current() {
106        assert_eq!(
107            KernelVersion::current().is_ok(),
108            true,
109            "{:?}",
110            KernelVersion::current()
111        );
112    }
113
114    #[test]
115    fn test_kernel_version_ord_eq() {
116        let v1 = KernelVersion::try_from("3.10.10").unwrap();
117        let v2 = KernelVersion::try_from("3.10.10").unwrap();
118        assert_eq!(v1, v2);
119    }
120
121    #[test]
122    fn test_kernel_version_ord_lt() {
123        let v1 = KernelVersion::try_from("2.10.10").unwrap();
124        let v2 = KernelVersion::try_from("3.10.10").unwrap();
125        assert_eq!(v1 < v2, true);
126    }
127}