#![doc(html_root_url = "https://docs.rs/fruit-salad/0.0.2")]
#![warn(clippy::pedantic, missing_docs)]
#![allow(clippy::semicolon_if_nothing_returned)]
#![no_std]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
#[cfg(doctest)]
pub mod readme {
doc_comment::doctest!("../README.md");
}
use core::{
any::TypeId,
cmp::Ordering,
fmt::{self, Debug, Display},
hash::{Hash, Hasher},
mem::{self, MaybeUninit},
ops::{Deref, DerefMut},
pin::Pin,
ptr::NonNull,
};
#[cfg(feature = "macros")]
pub use fruit_salad_proc_macro_definitions::{implement_dyncasts, Dyncast};
impl<'a> dyn 'a + Dyncast {
#[allow(missing_docs)]
#[must_use]
pub unsafe fn dyncast_<TActual: ?Sized, TStatic: 'static + ?Sized>(&self) -> Option<&TActual> {
self.__dyncast(
NonNull::new_unchecked(self as *const Self as *mut Self).cast(),
TypeId::of::<TStatic>(),
)
.map(|pointer_data| {
#[allow(clippy::cast_ptr_alignment)] pointer_data.as_ptr().cast::<&TActual>().read_unaligned()
})
}
#[allow(missing_docs)]
#[must_use]
pub unsafe fn dyncast_mut_<TActual: ?Sized, TStatic: 'static + ?Sized>(
&mut self,
) -> Option<&mut TActual> {
let this = NonNull::new_unchecked(self);
self.__dyncast(this.cast(), TypeId::of::<TStatic>())
.map(|pointer_data| {
pointer_data
.as_ptr()
.cast::<&mut TActual>()
.read_unaligned()
})
}
#[allow(missing_docs)]
#[must_use]
pub unsafe fn dyncast_pinned_<TActual: ?Sized, TStatic: 'static + ?Sized>(
self: Pin<&Self>,
) -> Option<Pin<&TActual>> {
self.__dyncast(
NonNull::new_unchecked(&*self as *const Self as *mut Self).cast(),
TypeId::of::<TStatic>(),
)
.map(|pointer_data| {
pointer_data
.as_ptr()
.cast::<Pin<&TActual>>()
.read_unaligned()
})
}
#[allow(missing_docs)]
#[must_use]
pub unsafe fn dyncast_pinned_mut_<TActual: ?Sized, TStatic: 'static + ?Sized>(
mut self: Pin<&mut Self>,
) -> Option<Pin<&mut TActual>> {
let this = NonNull::new_unchecked(Pin::into_inner_unchecked(self.as_mut()) as *mut Self);
self.__dyncast(this.cast(), TypeId::of::<TStatic>())
.map(|pointer_data| {
pointer_data
.as_ptr()
.cast::<Pin<&mut TActual>>()
.read_unaligned()
})
}
#[allow(missing_docs)]
#[must_use]
pub unsafe fn dyncast_ptr_<TActual: ?Sized, TStatic: 'static + ?Sized>(
this: NonNull<Self>,
) -> Option<NonNull<TActual>> {
this.as_ref()
.__dyncast(this.cast(), TypeId::of::<TStatic>())
.map(|pointer_data| {
pointer_data
.as_ptr()
.cast::<NonNull<TActual>>()
.read_unaligned()
})
}
#[allow(missing_docs)]
#[cfg(feature = "alloc")]
pub unsafe fn dyncast_box_<TActual: ?Sized, TStatic: 'static + ?Sized>(
self: alloc::boxed::Box<Self>,
) -> Result<alloc::boxed::Box<TActual>, alloc::boxed::Box<Self>> {
use alloc::boxed::Box;
let leaked = Box::into_raw(self);
(&*leaked)
.__dyncast(
NonNull::new_unchecked(leaked).cast(),
TypeId::of::<TStatic>(),
)
.map(|pointer_data| {
#[allow(clippy::cast_ptr_alignment)] Box::from_raw(
pointer_data
.as_ptr()
.cast::<*mut TActual>()
.read_unaligned(),
)
})
.ok_or_else(|| Box::from_raw(leaked))
}
#[allow(missing_docs)]
#[cfg(feature = "alloc")]
pub unsafe fn dyncast_pinned_box_<TActual: ?Sized, TStatic: 'static + ?Sized>(
self: Pin<alloc::boxed::Box<Self>>,
) -> Result<Pin<alloc::boxed::Box<TActual>>, Pin<alloc::boxed::Box<Self>>> {
use alloc::boxed::Box;
let leaked = Box::into_raw(Pin::into_inner_unchecked(self));
(&*leaked)
.__dyncast(
NonNull::new_unchecked(leaked).cast(),
TypeId::of::<TStatic>(),
)
.map(|pointer_data| {
#[allow(clippy::cast_ptr_alignment)] Pin::new_unchecked(Box::from_raw(
pointer_data
.as_ptr()
.cast::<*mut TActual>()
.read_unaligned(),
))
})
.ok_or_else(|| Pin::new_unchecked(Box::from_raw(leaked)))
}
}
impl dyn Dyncast {
#[allow(missing_docs)]
#[must_use]
pub fn dyncast<T: 'static + ?Sized>(&self) -> Option<&T> {
unsafe { self.dyncast_::<T, T>() }
}
#[allow(missing_docs)]
#[must_use]
pub fn dyncast_mut<T: 'static + ?Sized>(&mut self) -> Option<&mut T> {
unsafe { self.dyncast_mut_::<T, T>() }
}
#[allow(missing_docs)]
#[must_use]
pub fn dyncast_pinned<T: 'static + ?Sized>(self: Pin<&Self>) -> Option<Pin<&T>> {
unsafe { self.dyncast_pinned_::<T, T>() }
}
#[allow(missing_docs)]
#[must_use]
pub fn dyncast_pinned_mut<T: 'static + ?Sized>(self: Pin<&mut Self>) -> Option<Pin<&mut T>> {
unsafe { self.dyncast_pinned_mut_::<T, T>() }
}
#[allow(missing_docs)]
#[must_use]
pub unsafe fn dyncast_ptr<T: 'static + ?Sized>(this: NonNull<Self>) -> Option<NonNull<T>> {
Self::dyncast_ptr_::<T, T>(this)
}
#[allow(missing_docs)]
#[cfg(feature = "alloc")]
pub fn dyncast_box<T: 'static + ?Sized>(
self: alloc::boxed::Box<Self>,
) -> Result<alloc::boxed::Box<T>, alloc::boxed::Box<Self>> {
unsafe { self.dyncast_box_::<T, T>() }
}
#[allow(missing_docs)]
#[cfg(feature = "alloc")]
pub fn dyncast_pinned_box<T: 'static + ?Sized>(
self: Pin<alloc::boxed::Box<Self>>,
) -> Result<Pin<alloc::boxed::Box<T>>, Pin<alloc::boxed::Box<Self>>> {
unsafe { self.dyncast_pinned_box_::<T, T>() }
}
}
pub unsafe trait Dyncast {
#[doc(hidden)]
#[allow(clippy::type_complexity)]
fn __dyncast(
&self,
this: NonNull<()>,
target: TypeId,
) -> Option<MaybeUninit<[u8; mem::size_of::<&dyn Dyncast>()]>>;
}
impl<'a> Debug for dyn 'a + Dyncast {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("dyn Dyncast = ")?;
#[allow(clippy::option_if_let_else)] if let Some(debug) = unsafe { self.dyncast_::<dyn 'a + Debug, dyn Debug>() } {
debug.fmt(f)
} else {
f.write_str("!dyn Debug")
}
}
}
impl<'a> Display for dyn 'a + Dyncast {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[allow(clippy::option_if_let_else)] if let Some(display) = unsafe { self.dyncast_::<dyn 'a + Display, dyn Display>() } {
display.fmt(f)
} else {
f.write_str("dyn Dyncast = !dyn Display")
}
}
}
impl<'a> PartialEq for dyn 'a + Dyncast {
fn eq(&self, other: &Self) -> bool {
unsafe {
if let Some(this) =
Self::dyncast_::<dyn 'a + PartialEq<Self>, dyn PartialEq<dyn Dyncast>>(self)
{
this.eq(other)
} else if let Some(other) =
Self::dyncast_::<dyn 'a + PartialEq<Self>, dyn PartialEq<dyn Dyncast>>(other)
{
other.eq(self)
} else {
false
}
}
}
}
impl<'a> PartialOrd for dyn 'a + Dyncast {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
unsafe {
if let Some(this) =
Self::dyncast_::<dyn 'a + PartialOrd<Self>, dyn PartialOrd<dyn Dyncast>>(self)
{
this.partial_cmp(other)
} else if let Some(other) =
Self::dyncast_::<dyn 'a + PartialOrd<Self>, dyn PartialOrd<dyn Dyncast>>(other)
{
other.partial_cmp(self).map(Ordering::reverse)
} else {
None
}
}
}
}
pub trait DynHash {
fn dyn_hash(&self, state: &mut dyn Hasher);
}
impl<T: ?Sized> DynHash for T
where
T: Hash,
{
fn dyn_hash(&self, mut state: &mut dyn Hasher) {
<Self as Hash>::hash(self, &mut state)
}
}
impl Hash for dyn DynHash {
fn hash<H: Hasher>(&self, state: &mut H) {
self.dyn_hash(state)
}
}
impl<'a> Hash for dyn 'a + Dyncast {
fn hash<H: Hasher>(&self, state: &mut H) {
if let Some(dyn_hash) = unsafe { self.dyncast_::<dyn 'a + DynHash, dyn DynHash>() } {
dyn_hash.dyn_hash(state)
}
}
}
pub unsafe trait DynOrd {
fn concrete_type_id(&self) -> TypeId;
fn dyn_cmp(&self, other: &dyn DynOrd) -> Ordering;
}
impl<'a> PartialEq<dyn 'a + DynOrd> for dyn 'a + DynOrd {
fn eq(&self, other: &Self) -> bool {
self.dyn_cmp(other) == Ordering::Equal
}
}
impl<'a> PartialOrd<dyn 'a + DynOrd> for dyn 'a + DynOrd {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.dyn_cmp(other))
}
}
impl<'a> Eq for dyn 'a + DynOrd {}
impl<'a> Ord for dyn 'a + DynOrd {
fn cmp(&self, other: &Self) -> Ordering {
self.dyn_cmp(other)
}
}
mod private {
use crate::{Dyncast, DyncastEq};
pub trait Upcast<T: ?Sized> {
fn upcast(&self) -> &T;
fn upcast_mut(&mut self) -> &mut T;
}
impl<'a, T> Upcast<dyn 'a + Dyncast> for T
where
T: 'a + Dyncast,
{
fn upcast(&self) -> &(dyn 'a + Dyncast) {
self
}
fn upcast_mut(&mut self) -> &mut (dyn 'a + Dyncast) {
self
}
}
impl<'a, T> Upcast<dyn 'a + DyncastEq> for T
where
T: 'a + DyncastEq,
{
fn upcast(&self) -> &(dyn 'a + DyncastEq) {
self
}
fn upcast_mut(&mut self) -> &mut (dyn 'a + DyncastEq) {
self
}
}
}
use private::Upcast;
pub trait DyncastEq: Dyncast + for<'a> Upcast<dyn 'a + Dyncast> {
fn as_dyncast<'a>(&self) -> &(dyn 'a + Dyncast)
where
Self: 'a,
{
self.upcast()
}
fn as_dyncast_mut<'a>(&mut self) -> &mut (dyn 'a + Dyncast)
where
Self: 'a,
{
self.upcast_mut()
}
}
impl<'a> Deref for dyn 'a + DyncastEq {
type Target = dyn 'a + Dyncast;
fn deref(&self) -> &Self::Target {
self.as_dyncast()
}
}
impl<'a> DerefMut for dyn 'a + DyncastEq {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_dyncast_mut()
}
}
impl<'a> Debug for dyn 'a + DyncastEq {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(self.as_dyncast(), f)
}
}
impl<'a> Display for dyn 'a + DyncastEq {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(self.as_dyncast(), f)
}
}
impl<'a> PartialEq for dyn 'a + DyncastEq {
fn eq(&self, other: &Self) -> bool {
unsafe{self.dyncast_::<dyn 'a + PartialEq<dyn 'a + Dyncast>, dyn PartialEq<dyn Dyncast>>()}
.expect("Expected `Self` to be *dynamically* `dyn PartialEq<dyn Dyncast>` via `dyn DyncastOrd: PartialOrd`")
.eq(other.as_dyncast())
}
}
impl<'a> Eq for dyn 'a + DyncastEq {}
impl<'a> Hash for dyn 'a + DyncastEq {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_dyncast().hash(state)
}
}
pub trait DyncastOrd: DyncastEq + for<'a> Upcast<dyn 'a + DyncastEq> {
fn as_dyncast_eq<'a>(&self) -> &(dyn 'a + DyncastEq)
where
Self: 'a,
{
self.upcast()
}
fn as_dyncast_eq_mut<'a>(&mut self) -> &mut (dyn 'a + DyncastEq)
where
Self: 'a,
{
self.upcast_mut()
}
}
impl<'a> Deref for dyn 'a + DyncastOrd {
type Target = dyn 'a + DyncastEq;
fn deref(&self) -> &Self::Target {
self.as_dyncast_eq()
}
}
impl<'a> DerefMut for dyn 'a + DyncastOrd {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_dyncast_eq_mut()
}
}
impl<'a> Debug for dyn 'a + DyncastOrd {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(self.as_dyncast(), f)
}
}
impl<'a> Display for dyn 'a + DyncastOrd {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(self.as_dyncast(), f)
}
}
impl<'a> PartialEq for dyn 'a + DyncastOrd {
fn eq(&self, other: &Self) -> bool {
self.as_dyncast_eq().eq(other.as_dyncast_eq())
}
}
impl<'a> Eq for dyn 'a + DyncastOrd {}
impl<'a> PartialOrd for dyn 'a + DyncastOrd {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
unsafe{self.dyncast_::<dyn 'a+PartialOrd<dyn 'a+Dyncast>,dyn PartialOrd<dyn Dyncast>>()}
.expect("Expected `Self` to be *dynamically* `dyn PartialOrd<dyn Dyncast>` via `dyn DyncastOrd: PartialOrd`")
.partial_cmp(other.as_dyncast())
}
}
impl<'a> Ord for dyn 'a + DyncastOrd {
fn cmp(&self, other: &Self) -> Ordering {
unsafe{self.dyncast_::<dyn 'a+DynOrd, dyn DynOrd>()}
.expect("Expected `Self` to be *dynamically* `dyn PartialOrd<dyn DynOrd>` via `dyn DyncastOrd: Ord`")
.dyn_cmp(
unsafe{other
.dyncast_::<dyn 'a+DynOrd,dyn DynOrd>()}
.expect("Expected `other` to be *dynamically* `dyn PartialOrd<dyn DynOrd>` via `dyn DyncastOrd: Ord`")
)
}
}
impl<'a> Hash for dyn 'a + DyncastOrd {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_dyncast().hash(state)
}
}
#[doc(hidden)]
pub mod __ {
#[cfg(feature = "macros")]
pub use static_assertions::const_assert;
}