Skip to main content

winpty_rs_windows_version/
lib.rs

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