1use std::fmt::Write;
7
8#[repr(transparent)]
14#[derive(Clone, Copy, PartialEq, Eq, Debug)]
15pub struct PCWSTR(pub *const u16);
16
17impl AsRef<Self> for PCWSTR {
18 fn as_ref(&self) -> &Self {
19 self
20 }
21}
22
23impl windows_core::imp::TypeKind for PCWSTR {
24 type TypeKind = windows_core::imp::CopyType;
25}
26
27impl PCWSTR {
28 pub const fn from_raw(ptr: *const u16) -> Self {
30 Self(ptr)
31 }
32
33 pub const fn null() -> Self {
35 Self(core::ptr::null())
36 }
37
38 pub const fn as_ptr(&self) -> *const u16 {
40 self.0
41 }
42
43 pub fn is_null(&self) -> bool {
45 self.0.is_null()
46 }
47
48 pub unsafe fn len(&self) -> usize {
54 let mut len = 0;
55 let mut ptr = self.0;
56 while unsafe { ptr.read() } != 0 {
57 len += 1;
58 ptr = unsafe { ptr.add(1) };
59 }
60 len
61 }
62
63 pub unsafe fn is_empty(&self) -> bool {
69 unsafe { self.len() == 0 }
70 }
71
72 pub unsafe fn as_wide(&self) -> &[u16] {
78 unsafe { core::slice::from_raw_parts(self.0, self.len()) }
79 }
80}
81
82impl Default for PCWSTR {
83 fn default() -> Self {
84 Self::null()
85 }
86}
87
88#[derive(Clone, PartialEq, Eq, Default, Hash)]
92pub struct WString(Option<Vec<u16>>);
93const EMPTY: [u16; 1] = [0];
94
95impl WString {
96 pub const fn new() -> Self {
98 Self(None)
99 }
100
101 pub const fn is_empty(&self) -> bool {
103 self.0.is_none()
104 }
105
106 pub fn len(&self) -> usize {
108 match self.0.as_ref() {
109 Some(v) => v.len() - 1,
110 None => 0,
111 }
112 }
113
114 pub fn as_wide(&self) -> &[u16] {
116 match self.0.as_ref() {
117 Some(v) => {
118 v.as_slice().split_last().unwrap().1
120 }
121 None => &[],
122 }
123 }
124
125 pub fn to_string_lossy(&self) -> String {
127 String::from_utf16_lossy(self.as_wide())
128 }
129
130 pub fn as_ptr(&self) -> *const u16 {
132 match self.0.as_ref() {
133 Some(v) => v.as_ptr(),
134 None => EMPTY.as_ptr(), }
136 }
137
138 pub fn as_pcwstr(&self) -> PCWSTR {
140 match self.0.as_ref() {
141 Some(v) => PCWSTR::from_raw(v.as_ptr()),
142 None => PCWSTR::null(),
143 }
144 }
145
146 pub fn from_wide(value: &[u16]) -> Self {
148 unsafe { Self::from_wide_iter(value.iter().cloned(), value.len()) }
150 }
151
152 unsafe fn from_wide_iter<I: Iterator<Item = u16>>(iter: I, len: usize) -> Self {
153 if len == 0 {
154 return Self::new();
155 }
156 let iter = iter.chain(EMPTY.as_ref().iter().cloned());
158 let v = iter.collect::<Vec<_>>();
159 Self(Some(v))
160 }
161}
162
163impl From<&str> for WString {
164 fn from(value: &str) -> Self {
165 unsafe { Self::from_wide_iter(value.encode_utf16(), value.len()) }
168 }
169}
170
171impl From<String> for WString {
172 fn from(value: String) -> Self {
173 value.as_str().into()
174 }
175}
176impl From<&String> for WString {
177 fn from(value: &String) -> Self {
178 value.as_str().into()
179 }
180}
181
182impl From<&PCWSTR> for WString {
183 fn from(value: &PCWSTR) -> Self {
186 if value.is_null() {
187 Self::new()
188 } else {
189 Self::from_wide(unsafe { value.as_wide() })
190 }
191 }
192}
193
194impl From<PCWSTR> for WString {
195 fn from(value: PCWSTR) -> Self {
196 Self::from(&value)
197 }
198}
199
200impl From<Option<&WString>> for PCWSTR {
202 fn from(value: Option<&WString>) -> Self {
203 value.map_or_else(Self::null, WString::as_pcwstr)
204 }
205}
206
207impl core::fmt::Display for WString {
208 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
209 let wit = core::char::decode_utf16(self.as_wide().iter().cloned());
211 for c in wit {
212 match c {
213 Ok(c) => f.write_char(c)?,
214 Err(_) => f.write_char(core::char::REPLACEMENT_CHARACTER)?,
215 }
216 }
217 Ok(())
218 }
219}
220
221impl core::fmt::Debug for WString {
222 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223 write!(f, "\"{self}\"")
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use crate::PCWSTR;
230
231 use super::WString;
232
233 #[test]
234 fn string_test() {
235 let test_case = |s: &str| {
236 let h = WString::from(s);
237 assert_eq!(s.len(), h.len());
238 assert_eq!(s.is_empty(), h.is_empty());
239 assert_eq!(format!("{h}"), s);
240 assert_eq!(s, h.to_string_lossy());
241 assert_eq!(h.as_wide().len(), s.len());
242 let raw = h.as_ptr();
243 let h2 = WString::from(PCWSTR::from_raw(raw));
244 assert_eq!(s, h2.to_string_lossy());
245 assert_eq!(h, h2);
246 assert_ne!(h, WString::from("dummy"));
247 };
248
249 test_case("hello");
250 test_case("s");
251 test_case("");
252 }
253
254 #[test]
255 fn pcwstr_conversion_scans_utf16_code_units() {
256 let value = WString::from("a😀ω");
257 let raw = value.as_pcwstr();
258
259 assert_eq!(unsafe { raw.as_wide() }, value.as_wide());
260 assert_eq!(WString::from(raw), value);
261 }
262
263 #[test]
264 fn pcwstr_null_conversion_is_empty() {
265 let raw = PCWSTR::null();
266
267 assert!(raw.is_null());
268 assert_eq!(WString::from(raw), WString::new());
269 }
270
271 #[test]
272 fn wide_literal_returns_pal_pcwstr() {
273 const VALUE: PCWSTR = crate::w!("a😀ω");
274
275 assert_eq!(unsafe { VALUE.as_wide() }, WString::from("a😀ω").as_wide());
276 }
277
278 #[cfg(target_os = "linux")]
279 #[test]
280 fn upstream_pcwstr_uses_linux_wchar_width() {
281 use windows_core::PCWSTR as UpstreamPCWSTR;
282
283 #[repr(align(4))]
284 struct AlignedUtf16([u16; 4]);
285
286 let value = AlignedUtf16(['a' as u16, 'b' as u16, 0, 0]);
287 let raw = UpstreamPCWSTR::from_raw(value.0.as_ptr());
288
289 assert_eq!(unsafe { raw.len() }, 1);
290 assert_eq!(unsafe { raw.as_wide() }, &value.0[..1]);
291 }
292}