wslplugins_rs/user_distribution_id/
serde_impl.rs1use std::{ptr, slice};
8use windows_core::GUID;
9
10use crate::UserDistributionID;
11
12#[inline]
13const fn guid_to_windows_bytes(guid: &windows_core::GUID) -> &[u8] {
14 unsafe { slice::from_raw_parts(std::ptr::from_ref::<GUID>(guid).cast::<u8>(), 16) }
16}
17
18#[inline]
19const fn guid_from_windows_bytes(bytes: &[u8; 16]) -> GUID {
20 unsafe { ptr::read_unaligned(bytes.as_ptr().cast::<GUID>()) }
22}
23
24impl serde::Serialize for UserDistributionID {
25 #[inline]
26 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
27 where
28 S: serde::Serializer,
29 {
30 if serializer.is_human_readable() {
31 serializer.collect_str(self)
32 } else {
33 serializer.serialize_bytes(guid_to_windows_bytes(&self.0))
34 }
35 }
36}
37
38impl<'de> serde::Deserialize<'de> for UserDistributionID {
39 #[inline]
40 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
41 where
42 D: serde::Deserializer<'de>,
43 {
44 struct UserDistributionIDVisitor;
45
46 impl UserDistributionIDVisitor {
47 fn from_str<E>(value: &str) -> Result<UserDistributionID, E>
48 where
49 E: serde::de::Error,
50 {
51 value.parse::<UserDistributionID>().map_err(E::custom)
52 }
53
54 fn from_bytes<E>(bytes: &[u8]) -> Result<UserDistributionID, E>
55 where
56 E: serde::de::Error,
57 {
58 if bytes.len() != 16 {
59 return Err(E::invalid_length(bytes.len(), &"a 16-byte GUID"));
60 }
61
62 let bytes: [u8; 16] = bytes
63 .try_into()
64 .map_err(|_| E::custom("invalid GUID format in byte array"))?;
65 Ok(UserDistributionID(guid_from_windows_bytes(&bytes)))
66 }
67
68 fn from_seq<'de, A>(mut seq: A) -> Result<UserDistributionID, A::Error>
69 where
70 A: serde::de::SeqAccess<'de>,
71 {
72 let mut bytes = [0u8; 16];
73 for (index, slot) in bytes.iter_mut().enumerate() {
74 *slot = seq.next_element()?.ok_or_else(|| {
75 serde::de::Error::invalid_length(index, &"a 16-byte GUID")
76 })?;
77 }
78
79 if seq.next_element::<u8>()?.is_some() {
80 return Err(serde::de::Error::invalid_length(17, &"a 16-byte GUID"));
81 }
82
83 Ok(UserDistributionID(guid_from_windows_bytes(&bytes)))
84 }
85 }
86
87 impl<'de> serde::de::Visitor<'de> for UserDistributionIDVisitor {
88 type Value = UserDistributionID;
89
90 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 formatter.write_str("a GUID string or 16-byte GUID")
92 }
93
94 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
95 where
96 E: serde::de::Error,
97 {
98 Self::from_str(value)
99 }
100
101 fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
102 where
103 E: serde::de::Error,
104 {
105 Self::from_bytes(value)
106 }
107
108 fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
109 where
110 A: serde::de::SeqAccess<'de>,
111 {
112 Self::from_seq(seq)
113 }
114 }
115
116 if deserializer.is_human_readable() {
117 deserializer.deserialize_str(UserDistributionIDVisitor)
118 } else {
119 deserializer.deserialize_bytes(UserDistributionIDVisitor)
120 }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use crate::UserDistributionID;
127 use serde_test::{assert_tokens, Configure, Token};
128 use std::str::FromStr;
129 use windows_core::GUID;
130
131 #[test]
132 fn serde_roundtrip_uses_guid_string() {
133 let value = UserDistributionID(GUID::from_u128(0x12345678_9abc_def0_1357_2468ace0bdf1));
134 assert_tokens(
135 &value.readable(),
136 &[Token::Str("12345678-9ABC-DEF0-1357-2468ACE0BDF1")],
137 );
138 }
139
140 #[test]
141 fn serde_compact_uses_windows_guid_bytes() {
142 let value = UserDistributionID(GUID::from_u128(0x12345678_9abc_def0_1357_2468ace0bdf1));
143
144 assert_tokens(
145 &value.compact(),
146 &[Token::BorrowedBytes(&[
147 0x78, 0x56, 0x34, 0x12, 0xbc, 0x9a, 0xf0, 0xde, 0x13, 0x57, 0x24, 0x68, 0xac, 0xe0,
148 0xbd, 0xf1,
149 ])],
150 );
151 }
152
153 #[test]
154 fn binary_layout_matches_windows_guid_layout() {
155 let guid = GUID::from_u128(0x12345678_9abc_def0_1357_2468ace0bdf1);
156 assert_eq!(
157 super::guid_to_windows_bytes(&guid),
158 [
159 0x78, 0x56, 0x34, 0x12, 0xbc, 0x9a, 0xf0, 0xde, 0x13, 0x57, 0x24, 0x68, 0xac, 0xe0,
160 0xbd, 0xf1,
161 ]
162 );
163 }
164
165 #[test]
166 fn binary_deserialization_uses_windows_guid_layout() {
167 let bytes = [
168 0x78, 0x56, 0x34, 0x12, 0xbc, 0x9a, 0xf0, 0xde, 0x13, 0x57, 0x24, 0x68, 0xac, 0xe0,
169 0xbd, 0xf1,
170 ];
171 let guid = super::guid_from_windows_bytes(&bytes);
172 assert_eq!(
173 guid,
174 GUID::from_u128(0x12345678_9abc_def0_1357_2468ace0bdf1)
175 );
176 }
177
178 #[test]
179 fn binary_layout_matches_expected_bytes_for_known_guid() {
180 #[allow(clippy::unwrap_used)]
181 let id = UserDistributionID::from_str("80E4258D-0E16-4301-B8BE-E7833D02A7AA").unwrap();
182
183 assert_eq!(
184 super::guid_to_windows_bytes(&id.0),
185 [141, 37, 228, 128, 22, 14, 1, 67, 184, 190, 231, 131, 61, 2, 167, 170,]
186 );
187 }
188}