rebound 0.4.4

Full runtime reflection for Rust, with lifetime safety
Documentation
//! Dynamically typed, lifetime safe values

use crate::ty::CommonTypeInfo;
use crate::{Error, Reflected, Type};

use core::marker::PhantomData;
use core::ptr::NonNull;
use core::{fmt, mem};

use craft_eraser::{ErasedBox, ErasedNonNull};

#[derive(Debug)]
enum ValueKind {
    Owned(ErasedBox),
    Borrowed(ErasedNonNull),
    Moved,
}

/// Trait to represent a bound that a type may not outlives some given lifetime. Used to allow
/// sound borrowing of non-static Values.
///
/// If this was allowed, one could create a value containing a reference with a lifetime of `'a`,
/// then when they call `borrow`, the compiler would allow the creation of an output reference with
/// a lifetime of `'static`, which would be immediately unsound.
///
/// # Safety
///
/// Any implementation must ensure that all generics (lifetimes or types) in the type being
/// implemented on are correctly bounded to not outlive `'no`
pub unsafe trait NotOutlives<'no> {}

/// A Value represents a value with an erased type. It may be owned or borrowed.
/// The Value will have at most the lifetime of the value it was created from.
///
/// An owned Value will safely drop the contained object when it dies, while a borrowed Value
/// will not.
///
/// A Value may be borrowed out as a concrete type, though an attempt to do so
/// as a type not the same as the input type will result in a failure at runtime.
#[derive(Debug)]
pub struct Value<'a> {
    value: ValueKind,
    ty: Type,
    _phantom: PhantomData<&'a ()>,
}

#[allow(clippy::should_implement_trait)]
impl<'a> Value<'a> {
    /// Create a new owned Value from a pointer, with a lifetime no greater than that of the
    /// provided type.
    ///
    /// # Safety
    ///
    /// Similar to [`Box::from_raw`], this function may result in a double-free if called more than
    /// once with the same pointer. The pointer must also upheld any additional obligations on that
    /// function.
    pub unsafe fn from_ptr_owned<T: ?Sized + Reflected + 'a>(val: NonNull<T>) -> Value<'a> {
        Value {
            // SAFETY: Out safety requires the same guarantees
            value: ValueKind::Owned(ErasedBox::from_raw(val)),
            ty: Type::from::<T>(),
            _phantom: PhantomData,
        }
    }

    /// Create a new borrowed Value from a reference, with a lifetime no greater than that of the
    /// provided reference.
    pub fn from_ref<T: ?Sized + Reflected>(val: &T) -> Value<'_> {
        Value {
            value: ValueKind::Borrowed(ErasedNonNull::from(val)),
            ty: Type::from::<T>(),
            _phantom: PhantomData,
        }
    }

    /// Get a pointer to the raw backing metadata of this `Value`. It is the user's responsibility to not allow
    /// this pointer to outlive the lifetime of this `Value`.
    pub fn raw_meta(&self) -> NonNull<()> {
        match &self.value {
            ValueKind::Owned(b) => b.raw_meta_ptr(),
            ValueKind::Borrowed(p) => p.raw_meta_ptr(),
            ValueKind::Moved => unreachable!(),
        }
    }

    /// Get the raw backing pointer of this `Value`. It is the user's responsibility to not allow
    /// this pointer to outlive the lifetime of this `Value`.
    pub fn raw_ptr(&self) -> NonNull<()> {
        match &self.value {
            ValueKind::Owned(b) => b.raw_ptr(),
            ValueKind::Borrowed(p) => p.raw_ptr(),
            ValueKind::Moved => unreachable!(),
        }
    }

    /// Get the [`Type`] of this Value
    pub fn ty(&self) -> Type {
        self.ty
    }

    /// Attempt to move the contained T out of this Value in a fallible manner. This method is
    /// unsafe due to possible lifetime unsoundness, use [`Value::try_cast`] for a lifetime-safe
    /// variant.
    ///
    /// # Errors
    ///
    /// - This will fail if the Value is Borrowed with an [`Error::BorrowedValue`]
    /// - This will fail if the T isn't the same as the type of this value with [`Error::WrongType`]
    ///
    /// # Safety
    ///
    /// If this Value contains data which may not live forever, this function does not ensure that
    /// the provided T does *not* outlive `'a`. As such, it is the user's responsibility to not move
    /// data out of this value in a way which gives it a lifetime longer than its original.
    pub unsafe fn try_cast_unsafe<T: Reflected>(mut self) -> Result<T, (Self, Error)> {
        let value = mem::replace(&mut self.value, ValueKind::Moved);

        if let ValueKind::Owned(b) = value {
            if Type::from::<T>() == self.ty {
                Ok(*b.reify_box::<T>())
            } else {
                self.value = ValueKind::Owned(b);
                let ty = self.ty;
                Err((self, Error::wrong_type(Type::from::<T>(), ty)))
            }
        } else {
            self.value = value;
            Err((self, Error::BorrowedValue))
        }
    }

    /// Attempt to move the contained T out of this Value, panicking on failure. This will panic
    /// in all the cases that [`Value::try_cast_unsafe`] would return an Err value.
    ///
    /// # Panics
    ///
    /// If this value contains some type other than `T`, or if this is a borrowed value and as
    /// such cannot be moved out of
    ///
    /// # Safety
    ///
    /// See [`Value::try_cast_unsafe`]
    pub unsafe fn cast_unsafe<T: Reflected>(self) -> T {
        self.try_cast_unsafe()
            .unwrap_or_else(|_| panic!("Couldn't cast Value into type {}", T::name()))
    }

    /// Attempt to move the contained T out of this Value in a fallible manner.
    ///
    /// # Errors
    ///
    /// - This will fail if the Value is Borrowed with an [`Error::BorrowedValue`]
    /// - This will fail if the T isn't the same as the type of this value with [`Error::WrongType`]
    pub fn try_cast<T: Reflected + NotOutlives<'a>>(self) -> Result<T, (Self, Error)> {
        // SAFETY: Lifetimes are guaranteed safe by the `NotOutlives` bound
        unsafe { self.try_cast_unsafe() }
    }

    /// Attempt to move the contained T out of this Value, panicking on failure. This will panic
    /// in all the cases that [`Value::try_cast`] would return an Err value.
    ///
    /// # Panics
    ///
    /// If this value contains some type other than `T`, or if this is a borrowed value and as
    /// such cannot be moved out of
    ///
    pub fn cast<T: Reflected + NotOutlives<'a>>(self) -> T {
        self.try_cast()
            .unwrap_or_else(|_| panic!("Couldn't cast Value into type {}", T::name()))
    }

    // Should the returned references live for 'a? No. Why?
    // Assume an owned `Value<'static>` with ty of i32. A borrow out may live forever, however,
    // the Value will destroy the contained data when it goes out of scope. This means a user
    // could safely borrow a value, and then have it become invalid.

    /// Attempt to immutable borrow the T contained in this Value in a fallible manner.
    ///
    /// This will fail if the T isn't the same as the type of this value with [`Error::WrongType`]
    ///
    /// # Safety
    ///
    /// See [`Value::try_cast_unsafe`]
    pub unsafe fn try_borrow_unsafe<T: ?Sized + Reflected>(&self) -> Result<&T, Error> {
        if Type::from::<T>() == self.ty() {
            let ptr =
                NonNull::<T>::from_raw_parts(self.raw_ptr(), *self.raw_meta().cast().as_ref());
            Ok(ptr.as_ref())
        } else {
            Err(Error::wrong_type(Type::from::<T>(), self.ty()))
        }
    }

    /// Attempt to immutably borrow the T contained in this value, panicking on failure.
    ///
    /// # Panics
    ///
    /// In all cases that [`Value::try_borrow_unsafe`] would return an Err, if this value contains
    /// some type other than `T`
    ///
    /// # Safety
    ///
    /// See [`Value::try_cast_unsafe`]
    pub unsafe fn borrow_unsafe<T: ?Sized + Reflected>(&self) -> &T {
        self.try_borrow_unsafe()
            .unwrap_or_else(|_| panic!("Couldn't borrow Value as type {}", T::name()))
    }

    /// Attempt to immutably borrow the T contained by this Value in a fallible manner.
    ///
    /// This will fail if the T isn't the same as the type of this value with [`Error::WrongType`]
    ///
    /// # Examples
    ///
    /// Successful usage
    /// ```
    /// # use rebound::Value;
    /// let int = Value::from(1);
    /// assert!(int.try_borrow::<i32>().is_ok());
    /// ```
    ///
    /// Example failure
    /// ```
    /// # use rebound::Value;
    /// let int = Value::from(1);
    /// assert!(int.try_borrow::<&str>().is_err());
    /// ```
    pub fn try_borrow<'b, T: ?Sized + Reflected + NotOutlives<'a>>(
        &'b self,
    ) -> Result<&'b T, Error> {
        // SAFETY: Lifetimes are guaranteed safe by the `NotOutlives` bound
        unsafe { self.try_borrow_unsafe() }
    }

    /// Attempt to immutably borrow the T contained in this value, panicking on failure. This will
    /// panic in all the cases that [`Value::try_borrow`] would return an Err value.
    ///
    /// # Panics
    ///
    /// In all cases that [`Value::try_borrow`] would return an Err, if this value contains some
    /// type other than `T`
    ///
    /// # Example
    ///
    /// Successful usage
    /// ```
    /// # use rebound::Value;
    /// let bool = Value::from(true);
    /// let _ = bool.borrow::<bool>();
    /// ```
    ///
    /// Example failure
    /// ```should_panic
    /// # use rebound::Value;
    /// let bool = Value::from(true);
    /// let _ = bool.borrow::<char>();
    /// ```
    pub fn borrow<'b, T: ?Sized + Reflected + NotOutlives<'a>>(&'b self) -> &'b T {
        self.try_borrow()
            .unwrap_or_else(|_| panic!("Couldn't borrow Value as type {}", T::name()))
    }

    /// Attempt to mutably borrow the T contained by this Value in a fallible manner.
    ///
    /// This will fail if the T isn't the same as the type of this Value with [`Error::WrongType`]
    ///
    /// # Safety
    ///
    /// See [`Value::try_cast_unsafe`]
    pub unsafe fn try_borrow_unsafe_mut<T: ?Sized + Reflected>(&mut self) -> Result<&mut T, Error> {
        if Type::from::<T>() == self.ty() {
            let mut ptr =
                NonNull::<T>::from_raw_parts(self.raw_ptr(), *self.raw_meta().cast().as_ref());
            Ok(ptr.as_mut())
        } else {
            Err(Error::wrong_type(Type::from::<T>(), self.ty()))
        }
    }

    /// Attempt to mutably borrow the T contained in this value, panicking on failure. This will
    /// panic in all the cases that [`Value::try_borrow_mut`] would return an Err value.
    ///
    /// # Panics
    ///
    /// In all cases that [`Value::try_borrow_unsafe_mut`] would return an Err, if this value
    /// contains some type other than `T`
    ///
    /// # Safety
    ///
    /// See [`Value::try_cast_unsafe`]
    pub unsafe fn borrow_unsafe_mut<T: ?Sized + Reflected>(&mut self) -> &mut T {
        self.try_borrow_unsafe_mut()
            .unwrap_or_else(|_| panic!("Couldn't mutably borrow Value as type {}", T::name()))
    }

    /// Attempt to mutably borrow the T contained by this Value in a fallible manner.
    ///
    /// This will fail if the T isn't the same as the type of this Value with [`Error::WrongType`]
    ///
    /// # Example
    ///
    /// Successful usage
    /// ```
    /// # use rebound::Value;
    /// let mut char = Value::from('a');
    /// let c = char.try_borrow_mut::<char>()
    ///     .unwrap();
    /// *c = 'b';
    /// ```
    ///
    /// Example failure
    /// ```should_panic
    /// # use rebound::Value;
    /// let mut char = Value::from('a');
    /// let c = char.try_borrow_mut::<i32>()
    ///     .unwrap();
    /// *c = 2;
    ///
    /// ```
    pub fn try_borrow_mut<'b, T: ?Sized + Reflected + NotOutlives<'a>>(
        &'b mut self,
    ) -> Result<&'b mut T, Error> {
        // SAFETY: Lifetimes are guaranteed safe by the `NotOutlives` bound
        unsafe { self.try_borrow_unsafe_mut() }
    }

    /// Attempt to mutably borrow the T contained in this value, panicking on failure. This will
    /// panic in all the cases that [`Value::try_borrow_mut`] would return an Err value.
    ///
    /// # Panics
    ///
    /// In all cases that [`Value::try_borrow_mut`] would return an Err, if this value
    /// contains some type other than `T`
    ///
    /// # Example
    ///
    /// Successful usage
    /// ```
    /// # use rebound::Value;
    /// let mut str = Value::from("a string");
    /// let _ = str.borrow_mut::<&str>();
    /// ```
    ///
    /// Example failure
    /// ```should_panic
    /// # use rebound::Value;
    /// let mut str = Value::from("a string");
    /// let _ = str.borrow_mut::<&i32>();
    /// ```
    pub fn borrow_mut<'b, T: ?Sized + Reflected + NotOutlives<'a>>(&'b mut self) -> &'b mut T {
        self.try_borrow_mut()
            .unwrap_or_else(|_| panic!("Couldn't mutably borrow Value as type {}", T::name()))
    }

    /// If this Value is not a reference Type, get a Value containing an immutable reference to
    /// the data contained within this Value. A convenience function for operations that expect a
    /// reference to data you own.
    pub fn as_ref(&self) -> Result<Value<'_>, Error> {
        self.ty.as_ref(self)
    }

    /// If this Value is not a reference Type, get a Value containing a mutable reference to the
    /// data contained within this Value. A convenience function for operations that expect a
    /// mutable reference to data you own.
    pub fn as_mut(&mut self) -> Result<Value<'_>, Error> {
        let ty = self.ty;
        ty.as_mut(self)
    }
}

impl<'a> fmt::Pointer for Value<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Pointer::fmt(&self.raw_ptr(), f)
    }
}

impl<'a, T: Reflected + 'a> From<T> for Value<'a> {
    default fn from(val: T) -> Value<'a> {
        Value {
            value: ValueKind::Owned(ErasedBox::new(val)),
            ty: Type::from::<T>(),
            _phantom: PhantomData,
        }
    }
}