use core::fmt;
use core::{
any::{Any, TypeId, type_name},
fmt::Debug,
ptr,
};
use alloc::boxed::Box;
use crate::{Environment, View, layout::StretchAxis};
trait AnyViewImpl: 'static {
fn body(self: Box<Self>, env: Environment) -> AnyView;
fn type_id(&self) -> TypeId {
TypeId::of::<Self>()
}
fn name(&self) -> &'static str {
type_name::<Self>()
}
fn stretch_axis(&self) -> StretchAxis;
}
impl<T: View> AnyViewImpl for T {
fn body(self: Box<Self>, env: Environment) -> AnyView {
AnyView::new(View::body(*self, &env))
}
fn stretch_axis(&self) -> StretchAxis {
View::stretch_axis(self)
}
}
#[must_use]
pub struct AnyView(Box<dyn AnyViewImpl>);
impl Debug for AnyView {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("AnyView({})", self.name()))
}
}
impl Default for AnyView {
fn default() -> Self {
Self::new(())
}
}
impl AnyView {
pub fn new<V: View>(view: V) -> Self {
#[allow(clippy::missing_panics_doc)]
if TypeId::of::<V>() == TypeId::of::<Self>() {
let any = &mut Some(view) as &mut dyn Any;
return any
.downcast_mut::<Option<Self>>()
.expect("downcast to option should succeed")
.take()
.expect("option should contain a value"); }
Self(Box::new(view))
}
#[must_use]
pub fn is<T: 'static>(&self) -> bool {
self.type_id() == TypeId::of::<T>()
}
#[must_use]
pub fn type_id(&self) -> TypeId {
AnyViewImpl::type_id(&*self.0)
}
#[must_use]
pub fn name(&self) -> &'static str {
AnyViewImpl::name(&*self.0)
}
#[must_use]
pub fn stretch_axis(&self) -> StretchAxis {
AnyViewImpl::stretch_axis(&*self.0)
}
#[doc(hidden)]
#[must_use]
pub fn stable_ptr(&self) -> *const () {
ptr::from_ref::<dyn AnyViewImpl>(&*self.0).cast::<()>()
}
#[must_use]
pub unsafe fn downcast_unchecked<T: 'static>(self) -> Box<T> {
unsafe { Box::from_raw(Box::into_raw(self.0).cast::<T>()) }
}
#[must_use]
pub const unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T {
unsafe { &*(&raw const *self.0).cast::<T>() }
}
pub const unsafe fn downcast_mut_unchecked<T: 'static>(&mut self) -> &mut T {
unsafe { &mut *(&raw mut *self.0).cast::<T>() }
}
pub fn downcast<T: 'static>(self) -> Result<Box<T>, Self> {
if self.is::<T>() {
unsafe { Ok(self.downcast_unchecked()) }
} else {
Err(self)
}
}
#[must_use]
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
unsafe { self.is::<T>().then(|| self.downcast_ref_unchecked()) }
}
pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
unsafe { self.is::<T>().then(move || self.downcast_mut_unchecked()) }
}
}
impl View for AnyView {
fn body(self, env: &Environment) -> impl View {
self.0.body(env.clone())
}
fn stretch_axis(&self) -> StretchAxis {
AnyViewImpl::stretch_axis(&*self.0)
}
}
#[cfg(test)]
mod test {
use core::any::TypeId;
use super::AnyView;
#[test]
pub fn get_type_id() {
assert_eq!(AnyView::new(()).type_id(), TypeId::of::<()>());
}
}