Skip to main content

winstr/bstring/
safer.rs

1#![forbid(unsafe_code)]
2
3use crate::*;
4
5use winapi::shared::ntdef::LPCWSTR;
6
7use std::borrow::{Borrow, Cow};
8use std::cmp::Ordering;
9use std::ffi::{OsStr, OsString};
10use std::fmt::{self, Debug, Display, Formatter};
11use std::hash::{Hash, Hasher};
12use std::os::windows::ffi::{OsStrExt, OsStringExt};
13use std::path::{Path, PathBuf};
14
15
16
17#[cfg(feature = "display")]
18impl Display                for BString { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Display::fmt(&**self, fmt) } }
19impl Debug                  for BString { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Debug::fmt(&**self, fmt) } }
20impl AsRef<BStr>            for BString { fn as_ref(&self) -> &BStr { &**self } }
21impl AsRef<[u16]>           for BString { fn as_ref(&self) -> &[u16] { self.units() } }
22impl Borrow<BStr>           for BString { fn borrow(&self) -> &BStr { &**self } }
23impl Borrow<[u16]>          for BString { fn borrow(&self) -> &[u16] { self.units() } }
24impl Clone                  for BString { fn clone(&self) -> Self { Self::from_code_units(self.units().iter().cloned()).unwrap() } }
25impl From<&BStr>            for BString { fn from(value: &BStr      ) -> Self { Self::from_bstr(value).unwrap() } }
26impl From<&str>             for BString { fn from(value: &str       ) -> Self { Self::from_str(value).unwrap() } }
27impl From<&String>          for BString { fn from(value: &String    ) -> Self { Self::from_str(value).unwrap() } }
28impl From< String>          for BString { fn from(value:  String    ) -> Self { Self::from_str(&value).unwrap() } }
29impl From<&OsStr>           for BString { fn from(value: &OsStr     ) -> Self { Self::from_osstr(value).unwrap() } }
30impl From<&OsString>        for BString { fn from(value: &OsString  ) -> Self { Self::from_osstr(value).unwrap() } }
31impl From< OsString>        for BString { fn from(value:  OsString  ) -> Self { Self::from_osstr(&value).unwrap() } }
32impl Eq                     for BString {}
33impl Ord                    for BString { fn cmp(&self, other: &BString) -> Ordering { self.units().cmp(other.units()) } }
34impl Hash                   for BString { fn hash<H: Hasher>(&self, state: &mut H) { self.units().hash(state) } }
35
36#[cfg(feature = "display")]
37impl Display                for BStr    { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Display::fmt(&PathBuf::from(OsString::from_wide(self.units())).display(), fmt) } }
38impl Debug                  for BStr    { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Debug::fmt(&OsString::from_wide(self.units()), fmt) } }
39impl AsRef<BStr>            for BStr    { fn as_ref(&self) -> &BStr { self } }
40impl AsRef<[u16]>           for BStr    { fn as_ref(&self) -> &[u16] { self.units() } }
41impl Borrow<[u16]>          for BStr    { fn borrow(&self) -> &[u16] { self.units() } }
42impl Eq                     for &BStr   {}
43impl Ord                    for &BStr   { fn cmp(&self, other: &&BStr) -> Ordering { self.units().cmp(other.units()) } }
44impl Hash                   for &BStr   { fn hash<H: Hasher>(&self, state: &mut H) { self.units().hash(state) } }
45
46// Okay, this is a *lot* of traits.  I'm just mimicing the stdlib here though.
47//
48// Sliceable DST rules, using `str` as an example
49// 1.   Implement `&str == &str` (used for `"foo" == "bar"`)
50// 2.   Implement `str  ==  str` (used for `"foo"[..] == "bar"[..]`)
51// 3.   Skip `&str == str` / `str == &str`
52// 4.   Implement `&str == ...`
53// 5.   Implement `str  == ...`
54// 6.   Implement `... == &str`
55// 7.   Implement `... ==  str`
56// 8.   All the above for ordering comparisons too
57//
58// `&BStr` is slightly simpler than `&str` - it is not sliceable and cannot be directly used as a value
59
60macro_rules! peo {
61    ( &? $left:ty, $($tt:tt)* ) => {
62        peo!(& $left, $($tt)*);
63        peo!(  $left, $($tt)*);
64    };
65    ( $left:ty, &? $($tt:tt)* ) => {
66        peo!($left, & $($tt)*);
67        peo!($left,   $($tt)*);
68    };
69    ( $left:ty, $right:ty ) => {
70        impl PartialEq<$left> for $right {
71            fn eq(&self, other: &$left) -> bool {
72                self.utf16ish().eq(other.utf16ish())
73            }
74        }
75        impl PartialOrd<$left> for $right {
76            fn partial_cmp(&self, other: &$left) -> Option<Ordering> {
77                self.utf16ish().partial_cmp(other.utf16ish())
78            }
79        }
80    };
81}
82
83peo!(&BStr,   &BStr  );
84peo!(BString, BString);
85
86peo!(BString,           &BStr  ); peo!(&BStr,   BString         );
87peo!(&?[u16],           &BStr  ); peo!(&BStr,   &?[u16]         ); // useful for wchar::wch! comparisons
88peo!(&?str,             &BStr  ); peo!(&BStr,   &?str           );
89peo!(String,            &BStr  ); peo!(&BStr,   String          );
90peo!(&?OsStr,           &BStr  ); peo!(&BStr,   &?OsStr         );
91peo!(OsString,          &BStr  ); peo!(&BStr,   OsString        );
92peo!(&?Path,            &BStr  ); peo!(&BStr,   &?Path          );
93peo!(PathBuf,           &BStr  ); peo!(&BStr,   PathBuf         );
94peo!(Cow<'_, [u16]>,    &BStr  ); peo!(&BStr,   Cow<'_, [u16]>  );
95peo!(Cow<'_, str>,      &BStr  ); peo!(&BStr,   Cow<'_, str>    );
96peo!(Cow<'_, OsStr>,    &BStr  ); peo!(&BStr,   Cow<'_, OsStr>  );
97peo!(Cow<'_, Path>,     &BStr  ); peo!(&BStr,   Cow<'_, Path>   );
98
99//peo!(&BStr,           BString); peo!(BString, &BStr           ); // already covered
100peo!(&?str,             BString); peo!(BString, &?str           );
101peo!(&?[u16],           BString); peo!(BString, &?[u16]         );
102peo!(String,            BString); peo!(BString, String          );
103peo!(&?OsStr,           BString); peo!(BString, &?OsStr         );
104peo!(OsString,          BString); peo!(BString, OsString        );
105peo!(&?Path,            BString); peo!(BString, &?Path          );
106peo!(PathBuf,           BString); peo!(BString, PathBuf         );
107peo!(Cow<'_, [u16]>,    BString); peo!(BString, Cow<'_, [u16]>  );
108peo!(Cow<'_, str>,      BString); peo!(BString, Cow<'_, str>    );
109peo!(Cow<'_, OsStr>,    BString); peo!(BString, Cow<'_, OsStr>  );
110peo!(Cow<'_, Path>,     BString); peo!(BString, Cow<'_, Path>   );
111
112
113
114impl<'s> UTF16ish<'s> for BStr {
115    type Iter = std::iter::Copied<std::slice::Iter<'s, u16>>;
116    fn utf16ish(&'s self) -> Self::Iter { self.units().iter().copied() }
117}
118
119impl<'s> UTF16ish<'s> for BString {
120    type Iter = std::iter::Copied<std::slice::Iter<'s, u16>>;
121    fn utf16ish(&'s self) -> Self::Iter { self.units().iter().copied() }
122}
123
124
125
126impl BString {
127    /// Create a [BString] from a [str]
128    pub fn from_str(s: impl AsRef<str>) -> Option<Self> { Self::from_code_units(ESI::new(s.as_ref().encode_utf16())) }
129
130    /// Create a [BString] from a [OsStr]
131    pub fn from_osstr(s: impl AsRef<OsStr>) -> Option<Self> { Self::from_code_units(ESI::new(s.as_ref().encode_wide())) }
132
133    /// Create a [BString] from a [BStr]
134    pub fn from_bstr(s: impl AsRef<BStr>) -> Option<Self> { Self::from_code_units(s.as_ref().units().iter().copied()) }
135}
136
137
138
139impl BStr {
140    /// LPCWSTR / `* const wchar_t`
141    pub fn as_lpcwstr(&self) -> LPCWSTR { self.as_bstr() }
142
143    /// 32-bit length in [u16] unicode [code unit]s, including the implicit terminal `0u16`
144    ///
145    /// [code unit]:    https://unicode.org/glossary/#code_unit
146    pub fn len320(&self) -> u32 { self.len32() + 1 }
147
148    /// Length in [u16] unicode [code unit]s, excluding the implicit terminal `0u16`
149    ///
150    /// [code unit]:    https://unicode.org/glossary/#code_unit
151    #[cfg(not(target_pointer_width = "16"))]
152    pub fn len(&self) -> usize { self.len32() as usize }
153
154    /// Length in [u16] unicode [code unit]s, including the implicit terminal `0u16`
155    ///
156    /// [code unit]:    https://unicode.org/glossary/#code_unit
157    #[cfg(not(target_pointer_width = "16"))]
158    pub fn len0(&self) -> usize { self.len320() as usize }
159
160    /// The [u16] unicode [code unit]s of the string, excluding the terminal `0u16`
161    ///
162    /// [code unit]:    https://unicode.org/glossary/#code_unit
163    #[cfg(not(target_pointer_width = "16"))]
164    pub fn units(&self) -> &[u16] { let u = self.units0(); &u[..u.len()-1] }
165}
166
167
168
169/// "Exact Size Iterator" adapter
170struct ESI<I: Iterator> {
171    len:    usize,
172    iter:   I,
173}
174
175impl<I: Iterator + Clone> ESI<I> {
176    pub fn new(iter: I) -> Self {
177        Self {
178            len: iter.clone().count(),
179            iter,
180        }
181    }
182}
183
184impl<I: Iterator> Iterator for ESI<I> {
185    type Item = I::Item;
186    fn next(&mut self) -> Option<Self::Item> { self.iter.next() }
187}
188
189impl<I: Iterator> ExactSizeIterator for ESI<I> {
190    fn len(&self) -> usize { self.len }
191}