1#![allow(clippy::upper_case_acronyms)]
2use core::{fmt, hash};
3
4use crate::encode::{Encode, Encoding, RefEncode};
5
6#[cfg(all(not(feature = "gnustep-1-7"), not(feature = "unstable-objfw")))]
14mod inner {
15 #[cfg(any(
17 target_arch = "aarch64",
19 all(target_os = "ios", target_pointer_width = "64", not(target_abi_macabi)),
21 all(target_os = "tvos", target_pointer_width = "64"),
23 target_os = "watchos",
25 ))]
26 pub(crate) type BOOL = bool;
28
29 #[cfg(not(any(
31 target_arch = "aarch64",
32 all(target_os = "ios", target_pointer_width = "64", not(target_abi_macabi)),
33 all(target_os = "tvos", target_pointer_width = "64"),
34 target_os = "watchos",
35 )))]
36 pub(crate) type BOOL = i8;
38}
39
40#[cfg(all(
42 feature = "gnustep-1-7",
43 feature = "unstable-gnustep-strict-apple-compat"
44))]
45mod inner {
46 pub(crate) type BOOL = i8;
48}
49
50#[cfg(all(
51 feature = "gnustep-1-7",
52 not(feature = "unstable-gnustep-strict-apple-compat")
53))]
54mod inner {
55 #[cfg(all(windows, not(all(target_pointer_width = "64", target_env = "gnu"))))]
57 pub(crate) type BOOL = core::ffi::c_int;
58
59 #[cfg(not(all(windows, not(all(target_pointer_width = "64", target_env = "gnu")))))]
61 pub(crate) type BOOL = u8;
63}
64
65#[cfg(feature = "unstable-objfw")]
67mod inner {
68 pub(crate) type BOOL = i8;
72
73 }
76
77#[repr(transparent)]
96#[derive(Copy, Clone, Default)]
97pub struct Bool {
98 value: inner::BOOL,
99}
100
101impl Bool {
102 #[allow(clippy::unnecessary_cast)]
104 pub const YES: Self = Self::from_raw(true as inner::BOOL); #[allow(clippy::unnecessary_cast)]
108 pub const NO: Self = Self::from_raw(false as inner::BOOL); #[inline]
112 pub const fn new(value: bool) -> Self {
113 let value = value as inner::BOOL;
116 Self { value }
117 }
118
119 #[inline]
123 pub const fn from_raw(value: inner::BOOL) -> Self {
124 Self { value }
125 }
126
127 #[inline]
131 pub const fn as_raw(self) -> inner::BOOL {
132 self.value
133 }
134
135 #[inline]
139 pub const fn is_false(self) -> bool {
140 !self.as_bool()
141 }
142
143 #[inline]
147 pub const fn is_true(self) -> bool {
148 self.as_bool()
149 }
150
151 #[inline]
153 pub const fn as_bool(self) -> bool {
154 self.value != (false as inner::BOOL)
157 }
158}
159
160impl From<bool> for Bool {
161 #[inline]
162 fn from(b: bool) -> Bool {
163 Bool::new(b)
164 }
165}
166
167impl From<Bool> for bool {
168 #[inline]
169 fn from(b: Bool) -> bool {
170 b.as_bool()
171 }
172}
173
174impl fmt::Debug for Bool {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 f.write_str(if self.as_bool() { "YES" } else { "NO" })
177 }
178}
179
180impl PartialEq for Bool {
183 #[inline]
184 fn eq(&self, other: &Self) -> bool {
185 self.as_bool() == other.as_bool()
186 }
187}
188
189impl Eq for Bool {}
190
191impl hash::Hash for Bool {
192 #[inline]
193 fn hash<H: hash::Hasher>(&self, state: &mut H) {
194 self.as_bool().hash(state);
195 }
196}
197
198impl PartialOrd for Bool {
199 #[inline]
200 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
201 Some(self.cmp(other))
202 }
203}
204
205impl Ord for Bool {
206 #[inline]
207 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
208 self.as_bool().cmp(&other.as_bool())
209 }
210}
211
212trait Helper {
213 const __ENCODING: Encoding;
214}
215
216impl<T: Encode> Helper for T {
217 const __ENCODING: Encoding = T::ENCODING;
218}
219
220impl Helper for bool {
221 const __ENCODING: Encoding = Encoding::Bool;
222}
223
224unsafe impl Encode for Bool {
226 const ENCODING: Encoding = inner::BOOL::__ENCODING;
231}
232
233unsafe impl RefEncode for Bool {
244 const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250 use crate::__macro_helpers::{ConvertArgument, ConvertReturn};
251 use alloc::format;
252
253 #[test]
254 fn test_basic() {
255 let b = Bool::new(true);
256 assert!(b.as_bool());
257 assert!(b.is_true());
258 assert!(!b.is_false());
259 assert!(bool::from(b));
260 assert_eq!(b.as_raw() as usize, 1);
261
262 let b = Bool::new(false);
263 assert!(!b.as_bool());
264 assert!(!b.is_true());
265 assert!(b.is_false());
266 assert!(!bool::from(b));
267 assert_eq!(b.as_raw() as usize, 0);
268 }
269
270 #[test]
271 fn test_associated_constants() {
272 let b = Bool::YES;
273 assert!(b.as_bool());
274 assert!(b.is_true());
275 assert_eq!(b.as_raw() as usize, 1);
276
277 let b = Bool::NO;
278 assert!(!b.as_bool());
279 assert!(b.is_false());
280 assert_eq!(b.as_raw() as usize, 0);
281 }
282
283 #[test]
284 fn test_encode() {
285 assert_eq!(bool::__ENCODING, Encoding::Bool);
286
287 assert_eq!(
288 <bool as ConvertArgument>::__Inner::__ENCODING,
289 <bool as ConvertArgument>::__Inner::ENCODING
290 );
291 assert_eq!(
292 <bool as ConvertReturn<()>>::Inner::__ENCODING,
293 <bool as ConvertReturn<()>>::Inner::ENCODING
294 );
295 }
296
297 #[test]
298 fn test_impls() {
299 let b: Bool = Default::default();
300 assert!(!b.as_bool());
301 assert!(b.is_false());
302
303 assert!(Bool::from(true).as_bool());
304 assert!(Bool::from(true).is_true());
305 assert!(Bool::from(false).is_false());
306
307 assert!(Bool::from(true).is_true());
308 assert!(Bool::from(false).is_false());
309
310 assert_eq!(Bool::new(true), Bool::new(true));
311 assert_eq!(Bool::new(false), Bool::new(false));
312
313 assert!(Bool::new(false) < Bool::new(true));
314 }
315
316 #[test]
317 fn test_debug() {
318 assert_eq!(format!("{:?}", Bool::from(true)), "YES");
319 assert_eq!(format!("{:?}", Bool::from(false)), "NO");
320 }
321
322 #[test]
323 #[cfg(all(target_vendor = "apple", target_os = "macos", target_arch = "x86_64"))]
325 fn test_outside_normal() {
326 let b = Bool::from_raw(42);
327 assert!(b.is_true());
328 assert!(!b.is_false());
329 assert_eq!(b.as_raw(), 42);
330
331 assert_eq!(b, Bool::new(true));
333 assert_ne!(b, Bool::new(false));
334 }
335}