use std::ops::Deref; use std::sync::{Condvar, OnceLock, PoisonError}; use kludgine::app::winit::event::Modifiers; use kludgine::app::winit::keyboard::ModifiersState; /// This [`Condvar`] is a wrapper that on Mac OS/iOS asserts unwind safety. On /// all other platforms, this is a transparent wrapper around `Condvar`. See /// for more information. #[derive(Debug, Default)] pub struct UnwindsafeCondvar( #[cfg(any(target_os = "ios", target_os = "macos"))] std::panic::AssertUnwindSafe, #[cfg(not(any(target_os = "ios", target_os = "macos")))] Condvar, ); impl Deref for UnwindsafeCondvar { type Target = Condvar; fn deref(&self) -> &Self::Target { &self.0 } } impl UnwindsafeCondvar { pub const fn new() -> Self { #[cfg(any(target_os = "ios", target_os = "macos"))] { Self(AssertUnwindSafe(Condvar::new())) } #[cfg(not(any(target_os = "ios", target_os = "macos")))] { Self(Condvar::new()) } } } /// Invokes the provided macro with a pattern that can be matched using this /// `macro_rules!` expression: `$($type:ident $field:tt $var:ident),+`, where `$type` is an /// identifier to use for the generic parameter and `$field` is the field index /// inside of the tuple. macro_rules! impl_all_tuples { ($macro_name:ident) => { $macro_name!(T0 0 t0); $macro_name!(T0 0 t0, T1 1 t1); $macro_name!(T0 0 t0, T1 1 t1, T2 2 t2); $macro_name!(T0 0 t0, T1 1 t1, T2 2 t2, T3 3 t3); $macro_name!(T0 0 t0, T1 1 t1, T2 2 t2, T3 3 t3, T4 4 t4); $macro_name!(T0 0 t0, T1 1 t1, T2 2 t2, T3 3 t3, T4 4 t4, T5 5 t5); } } /// Invokes a function with a clone of `self`. pub trait WithClone: Sized { /// The type that results from cloning. type Cloned; /// Maps `with` with the results of cloning `self`. fn with_clone(&self, with: impl FnOnce(Self::Cloned) -> R) -> R; } macro_rules! impl_with_clone { ($($name:ident $field:tt $var:ident),+) => { impl<'a, $($name: Clone,)+> WithClone for ($(&'a $name,)+) { type Cloned = ($($name,)+); fn with_clone(&self, with: impl FnOnce(Self::Cloned) -> R) -> R { with(($(self.$field.clone(),)+)) } } }; } impl<'a, T> WithClone for &'a T where T: Clone, { type Cloned = T; fn with_clone(&self, with: impl FnOnce(Self::Cloned) -> R) -> R { with((*self).clone()) } } impl_all_tuples!(impl_with_clone); pub trait ModifiersExt { fn primary(&self) -> bool; fn word_select(&self) -> bool; fn possible_shortcut(&self) -> bool; } impl ModifiersExt for ModifiersState { #[cfg(any(target_os = "macos", target_os = "ios"))] fn primary(&self) -> bool { self.super_key() } #[cfg(not(any(target_os = "macos", target_os = "ios")))] fn primary(&self) -> bool { self.control_key() } #[cfg(any(target_os = "macos", target_os = "ios"))] fn word_select(&self) -> bool { self.alt_key() } #[cfg(not(any(target_os = "macos", target_os = "ios")))] fn word_select(&self) -> bool { self.control_key() } fn possible_shortcut(&self) -> bool { self.control_key() || self.alt_key() || self.super_key() } } impl ModifiersExt for Modifiers { fn primary(&self) -> bool { self.state().primary() } fn word_select(&self) -> bool { self.state().word_select() } fn possible_shortcut(&self) -> bool { self.state().word_select() } } /// A [`OnceLock`]-based lazy initializer. pub struct Lazy { init: fn() -> T, once: OnceLock, } impl Lazy { /// Returns a type that initializes itself once upon being accessed. /// /// `init` is guaranteed to be called only once, but this type can't accept /// `FnOnce` generic types due to being unable to allocate a `Box` in /// `const` or being able to give a name to the type of a function so that /// users could use this type in static variables. pub const fn new(init: fn() -> T) -> Self { Self { init, once: OnceLock::new(), } } } impl Deref for Lazy { type Target = T; fn deref(&self) -> &Self::Target { self.once.get_or_init(self.init) } } pub trait IgnorePoison { type Unwrapped; fn ignore_poison(self) -> Self::Unwrapped; } impl IgnorePoison for Result> { type Unwrapped = T; fn ignore_poison(self) -> Self::Unwrapped { self.map_or_else(PoisonError::into_inner, |g| g) } }