lfs_core/device_id/
windows.rs1use {
2 super::{
3 ParseDeviceIdError,
4 ParseDeviceIdSnafu,
5 },
6 crate::WindowsApiSnafu,
7 snafu::prelude::*,
8 std::{
9 ffi::OsStr,
10 fmt,
11 os::windows::ffi::OsStrExt,
12 path::Path,
13 str::FromStr,
14 },
15 windows::{
16 Win32::Storage::FileSystem::{
17 GetVolumeInformationW,
18 GetVolumeNameForVolumeMountPointW,
19 GetVolumePathNameW,
20 },
21 core::PCWSTR,
22 },
23};
24#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
26pub struct DeviceId {
27 pub serial: u32,
28}
29
30impl fmt::Display for DeviceId {
31 fn fmt(
32 &self,
33 f: &mut fmt::Formatter,
34 ) -> fmt::Result {
35 write!(f, "{:X}-{:X}", self.serial >> 16, self.serial & 0x0000_FFFF)
36 }
37}
38
39impl FromStr for DeviceId {
40 type Err = ParseDeviceIdError;
41
42 fn from_str(string: &str) -> Result<Self, Self::Err> {
43 if let Some((high, low)) = string.split_once('-') {
44 if let (Ok(high), Ok(low)) =
45 (u32::from_str_radix(high, 16), u32::from_str_radix(low, 16))
46 {
47 let serial = (high << 16) | low;
48 return Ok(Self { serial });
49 }
50 }
51
52 u32::from_str_radix(string, 16)
53 .ok()
54 .map(|serial| Self { serial })
55 .with_context(|| ParseDeviceIdSnafu { string })
56 }
57}
58
59impl From<u64> for DeviceId {
60 fn from(num: u64) -> Self {
61 Self { serial: num as u32 }
62 }
63}
64
65impl From<u32> for DeviceId {
66 fn from(num: u32) -> Self {
67 Self { serial: num }
68 }
69}
70
71impl DeviceId {
72 pub fn new(serial: u32) -> Self {
73 Self { serial }
74 }
75 pub fn of_path(path: &Path) -> Result<Self, crate::Error> {
77 unsafe {
78 let path_wide: Vec<u16> = OsStr::new(path)
79 .encode_wide()
80 .chain(std::iter::once(0)) .collect();
82
83 let mut volume_path_buf = vec![0u16; 260]; GetVolumePathNameW(PCWSTR(path_wide.as_ptr()), &mut volume_path_buf).context(
86 WindowsApiSnafu {
87 api: "GetVolumePathNameW",
88 },
89 )?;
90
91 let mut volume_guid_buf = vec![0u16; 260]; GetVolumeNameForVolumeMountPointW(
94 PCWSTR(volume_path_buf.as_ptr()),
95 &mut volume_guid_buf,
96 )
97 .context(WindowsApiSnafu {
98 api: "GetVolumeNameForVolumeMountPointW",
99 })?;
100
101 let mut serial: u32 = 0;
103 GetVolumeInformationW(
104 PCWSTR(volume_guid_buf.as_ptr()),
105 None,
106 Some(&mut serial),
107 None,
108 None,
109 None,
110 )
111 .context(WindowsApiSnafu {
112 api: "GetVolumeInformationW",
113 })?;
114
115 Ok(Self { serial })
116 }
117 }
118}
119
120#[test]
121fn test_from_str() {
122 assert_eq!(
123 DeviceId::new(0xABCD_1234),
124 DeviceId::from_str("ABCD-1234").unwrap()
125 );
126 assert_eq!(
127 DeviceId::new(0xABCD_1234),
128 DeviceId::from_str("ABCD1234").unwrap()
129 );
130}
131
132#[test]
133fn test_from_u64() {
134 assert_eq!(
135 DeviceId::new(0xFFFF_FFFF),
136 DeviceId::from(0xFFFF_FFFF_FFFFu64)
137 );
138}