multi-any 0.1.1

`MultiAny` is just like `Any` but can downcast to trait objects.
Documentation
use crate::Meta;
use crate::TypedMetadata;
use crate::ptr_metadata::*;
use std::any::Any;
use std::any::TypeId;

/// A trait for dynamic typing that allows downcasting to both concrete types and trait objects.
///
/// Similar to [`std::any::Any`], but supports downcasting to traits implemented by the type.
///
/// Most users should implement this trait via the `#[derive(MultiAny)]` macro.
///
/// # Examples
///
/// ```
/// use multi_any::*;
///
/// // Traits must be annotated with `#[multi_any]`
/// // With "nightly" feature this is not required
/// #[multi_any]
/// trait Trait1 {}
/// #[multi_any]
/// trait Trait2 {}
///
/// // MultiAny can be derived, and implemented traits must be specified
/// #[derive(MultiAny)]
/// #[multi_any(Trait1, Trait2)]
/// struct Foo;
///
/// impl Trait1 for Foo {}
/// impl Trait2 for Foo {}
///
/// // convert Box<Foo> to Box<dyn MultiAny>
/// let foo: Box<dyn MultiAny> = Box::new(Foo);
///
/// // downcast to concrete type
/// let foo_ref: &Foo = foo.downcast_ref().unwrap();
///
/// // downcast to trait object
/// let trait_ref = foo.downcast_ref::<dyn Trait1>().unwrap();
///
/// // downcast to Box
/// let foo: Box<dyn Trait1> = foo.downcast().unwrap();
/// ```
pub trait MultiAny: Any {
    /// Returns type-erased metadata for the requested type.
    ///
    /// # Parameters
    /// - `type_id`: The `TypeId` of the requested type or trait object.
    ///
    /// # Returns
    /// - `Some(Meta)` if the type matches the concrete type or a trait implemented by the object.
    /// - `None` if the type does not match.
    ///
    /// Implementors must ensure that the returned `Meta` matches the actual data type and metadata type.
    fn get_metadata(&self, type_id: TypeId) -> Option<Meta>;
}

impl dyn MultiAny {
    /// Attempts to downcast a `MultiAny` object to a reference of type `RequestedType`.
    ///
    /// Returns `Some(&RequestedType)` if the object can be interpreted as the requested type,
    /// or `None` otherwise.
    ///
    /// # Type Parameters
    /// - `RequestedType`: The concrete type or trait object to downcast to.
    ///
    /// # Panics
    /// - Can panic with invalid implementation of `get_metadata` (Derived implementation is guaranteed to be valid.)
    pub fn downcast_ref<RequestedType>(&self) -> Option<&RequestedType>
    where
        RequestedType: Pointee + ?Sized + 'static,
        RequestedType::Metadata: TypedMetadata,
    {
        let meta = self.get_metadata(TypeId::of::<RequestedType>())?;
        assert_eq!(meta.data_id, self.type_id(), "Wrong Data type");

        let typed_meta = RequestedType::Metadata::from_meta(meta);
        let data_pointer = self as *const dyn MultiAny as *const ();

        // SAFETY: We are merging raw data pointer with metadata.
        // This is safe because we know that data type matches the type this metadata was created for.
        let ptr: *const RequestedType = from_raw_parts(data_pointer, typed_meta);

        // SAFETY: We are converting raw pointer to reference.
        // This is safe because pointer points to the same data as self.
        unsafe { &*ptr }.into()
    }

    /// Attempts to downcast a `MultiAny` object to a mutable reference of type `RequestedType`.
    ///
    /// Returns `Some(&mut RequestedType)` if the object can be interpreted as the requested type,
    /// or `None` otherwise.
    ///
    /// # Type Parameters
    /// - `RequestedType`: The concrete type or trait object to downcast to.
    ///
    /// # Panics
    /// - Can panic with invalid implementation of `get_metadata` (Derived implementation is guaranteed to be valid.)
    pub fn downcast_mut<RequestedType>(&mut self) -> Option<&mut RequestedType>
    where
        RequestedType: Pointee + ?Sized + 'static,
        RequestedType::Metadata: TypedMetadata,
    {
        let ptr = self.downcast_mut_ptr::<RequestedType>()?;

        // SAFETY: We are converting raw pointer to mutable reference.
        // This is safe because pointer points to the same data as self.
        unsafe { &mut *ptr }.into()
    }

    /// Attempts to downcast a `Box<dyn MultiAny>` to a `Box<RequestedType>`.
    ///
    /// Returns `Ok(Box<RequestedType>)` if the object can be interpreted as the requested type,
    /// or `Err(Box<dyn MultiAny>)` otherwise. This is a consuming operation.
    ///
    /// # Type Parameters
    /// - `RequestedType`: The concrete type or trait object to downcast to.
    ///
    /// # Panics
    /// - Can panic with invalid implementation of `get_metadata` (Derived implementation is guaranteed to be valid.)
    pub fn downcast<RequestedType>(mut self: Box<Self>) -> Result<Box<RequestedType>, Box<Self>>
    where
        RequestedType: Pointee + ?Sized + 'static,
        RequestedType::Metadata: TypedMetadata,
    {
        let Some(ptr) = self.downcast_mut_ptr::<RequestedType>() else {
            return Err(self);
        };

        // We already have the pointer to data. Leak the box to avoid drop.
        Box::leak(self);

        // SAFETY: We are creating a box from the raw pointer.
        // This is safe because the pointer points to data allocated by Box with the same data type, but with different metadata.
        Ok(unsafe { Box::from_raw(ptr) })
    }

    fn downcast_mut_ptr<RequestedType>(&mut self) -> Option<*mut RequestedType>
    where
        RequestedType: Pointee + ?Sized + 'static,
        RequestedType::Metadata: TypedMetadata,
    {
        let meta = self.get_metadata(TypeId::of::<RequestedType>())?;
        assert_eq!(meta.data_id, (*self).type_id(), "Wrong Data type");

        let typed_meta = RequestedType::Metadata::from_meta(meta);
        let data_pointer = self as *mut dyn MultiAny as *mut ();

        // SAFETY: We are merging raw data pointer with metadata.
        // This is safe because we know that data type matches the type this metadata was created for.
        let ptr: *mut RequestedType = from_raw_parts_mut(data_pointer, typed_meta);

        Some(ptr)
    }
}

impl std::fmt::Debug for dyn MultiAny {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("MultiAny").finish()
    }
}