windows_version/
lib.rs

1#![doc = include_str!("../readme.md")]
2#![cfg(windows)]
3#![cfg_attr(not(test), no_std)]
4#![allow(non_snake_case, clippy::upper_case_acronyms)]
5
6mod bindings;
7use bindings::*;
8
9/// Operating system version information.
10#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
11pub struct OsVersion {
12    /// The major version number of the operating system.
13    pub major: u32,
14
15    /// The minor version number of the operating system.
16    pub minor: u32,
17
18    /// The major version number of the latest service pack installed on the system.
19    pub pack: u32,
20
21    /// The build number of the operating system.
22    pub build: u32,
23}
24
25impl OsVersion {
26    /// Creates a new `OsVersion` with the given values.
27    pub const fn new(major: u32, minor: u32, pack: u32, build: u32) -> Self {
28        Self {
29            major,
30            minor,
31            pack,
32            build,
33        }
34    }
35
36    /// Gets the version information of the currently running operating system.
37    #[cfg(not(test))]
38    pub fn current() -> Self {
39        let mut info = OSVERSIONINFOEXW::new();
40
41        unsafe {
42            RtlGetVersion(&mut info as *mut _ as *mut _);
43        }
44
45        Self {
46            major: info.dwMajorVersion,
47            minor: info.dwMinorVersion,
48            pack: info.wServicePackMajor as u32,
49            build: info.dwBuildNumber,
50        }
51    }
52
53    /// Hook used for testing `ge`.
54    #[cfg(test)]
55    fn current() -> Self {
56        test::test_current()
57    }
58}
59
60/// Determines if the currently running operating system is a Windows Server release.
61pub fn is_server() -> bool {
62    let mut info = OSVERSIONINFOEXW::new();
63
64    unsafe {
65        RtlGetVersion(&mut info as *mut _ as *mut _);
66    }
67
68    info.wProductType as u32 != VER_NT_WORKSTATION
69}
70
71impl OSVERSIONINFOEXW {
72    fn new() -> Self {
73        Self {
74            dwOSVersionInfoSize: core::mem::size_of::<Self>() as u32,
75            ..Default::default()
76        }
77    }
78}
79
80#[cfg(test)]
81#[allow(clippy::nonminimal_bool)] // explicit logic is intentionally being tested
82mod test {
83    use super::OsVersion;
84    use std::sync::RwLock;
85
86    static TEST_CURRENT: RwLock<OsVersion> = RwLock::new(OsVersion::new(0, 0, 0, 0));
87
88    pub fn test_current() -> OsVersion {
89        *TEST_CURRENT.read().unwrap()
90    }
91
92    fn set_current(version: OsVersion) {
93        *TEST_CURRENT.write().unwrap() = version;
94    }
95
96    #[test]
97    fn test() {
98        assert_eq!(OsVersion::current(), OsVersion::new(0, 0, 0, 0));
99
100        set_current(OsVersion::new(1, 2, 3, 4));
101        assert_eq!(OsVersion::current(), OsVersion::new(1, 2, 3, 4));
102
103        set_current(OsVersion::new(10, 0, 0, 0));
104        assert!(OsVersion::current() >= OsVersion::new(9, 0, 0, 0));
105        assert!(OsVersion::current() >= OsVersion::new(10, 0, 0, 0));
106        assert!(!(OsVersion::current() >= OsVersion::new(11, 0, 0, 0)));
107
108        set_current(OsVersion::new(10, 100, 0, 0));
109        assert!(OsVersion::current() >= OsVersion::new(10, 99, 0, 0));
110        assert!(OsVersion::current() >= OsVersion::new(10, 100, 0, 0));
111        assert!(!(OsVersion::current() >= OsVersion::new(10, 101, 0, 0)));
112
113        set_current(OsVersion::new(10, 100, 1000, 0));
114        assert!(OsVersion::current() >= OsVersion::new(10, 100, 999, 0));
115        assert!(OsVersion::current() >= OsVersion::new(10, 100, 1000, 0));
116        assert!(!(OsVersion::current() >= OsVersion::new(10, 100, 1001, 0)));
117
118        set_current(OsVersion::new(10, 100, 1_000, 10_000));
119        assert!(OsVersion::current() >= OsVersion::new(10, 100, 1_000, 9_999));
120        assert!(OsVersion::current() >= OsVersion::new(10, 100, 1_000, 10_000));
121        assert!(!(OsVersion::current() >= OsVersion::new(10, 100, 1_000, 10_001)));
122    }
123}