structural 0.4.3

Field accessor traits,and emulation of structural types.
Documentation
//! Items for moving out fields out of a type,and dropping it afterwards.

mod on_drop;

pub use self::on_drop::{IntoFieldsWrapper, RunDrop, RunDropFields, RunPostDrop};

/////////////////////////////////////////////////////////////////////////////////

/// For use in macros,
/// to add code that runs before before and after the macro-generated
/// [`DropFields::drop_fields`] code.
///
/// # Safety
///
/// ### Implementing `pre_drop`
///
/// In the implementation of `pre_drop`,
/// you must not read any fields that could have been moved
/// (using the `move_out_*field` methods from `Into*Field`).
///
/// ### Implementing `post_drop`
///
/// In the implementation of `post_drop`,you must only read Copy fields
/// (because all non-Copy fields were dropped).
///
/// # Example
///
/// This example demonstrates how you can use `PrePostDropFields`,
/// as well as `DropFields::pre_move`.
///
/// ```rust
/// use structural::{fp, StructuralExt, Structural};
/// use structural::field::PrePostDropFields;
///
/// {
///     let mut vector=Vec::new();
///     drop(WithDropLogic{
///         vector: &mut vector,
///         x: 0,
///         y: 0,
///         z: 0
///     });
///     // The vector was written to in WithDropLogic's impl of `Drop::drop`
///     assert_eq!(vector, [1000]);
/// }
/// {
///     let mut vector=Vec::new();
///     let this=WithDropLogic{
///         vector: &mut vector,
///         x: 3,
///         y: 5,
///         z: 8
///     };
///     assert_eq!(into_xyz(this), (3, 5, 8));
///
///     // The order in which things happen is:
///     // - `DropFields::pre_move`pushes 2001 into `vector`
///     // - The x,y,and z fields are moved out
///     // - `PrePostDropFields::pre_drop`: pushes 2002 into `vector`
///     // - The non-moved-out fields are dropped,
///     //   in this case only the `vector` field wasn't moved out,
///     //   because there's no accessor impl to get it.
///     // - `PrePostDropFields::post_drop`: pushes 2002 into `vector`
///     // - The fields are returned
///     assert_eq!(vector, [2001,2002,2003]);
/// }
/// {
///     let this=Variables{
///         x: 13,
///         y: 21,
///         z: 34,
///     };
///     assert_eq!(into_xyz(this), (13, 21, 34));
/// }
///
/// // The `Variables_SI` trait was generated by the `Structural` derive on `Variables`,
/// // aliasing its accessor trait impls.
/// fn into_xyz(this: impl Variables_SI)->(u32,u32,u32) {
///     this.into_fields(fp!(x,y,z))
/// }
///
///
/// #[derive(Structural)]
/// struct Variables{
///     pub x: u32,
///     pub y: u32,
///     pub z: u32,
/// }
///
/// #[derive(Structural)]
/// # #[struc(no_trait)]
/// #[struc(pre_move="WithDropLogic::drop_")]
/// #[struc(pre_post_drop_fields)]
/// struct WithDropLogic<'a>{
///     vector: &'a mut Vec<u32>,
///     pub x: u32,
///     pub y: u32,
///     pub z: u32,
/// }
///
/// impl WithDropLogic<'_>{
///     fn drop_(&mut self){
///         self.vector.push(2001);
///     }
/// }
///
/// unsafe impl PrePostDropFields for WithDropLogic<'_> {
///     unsafe fn pre_drop(this: *mut Self) {
///         let Self{ref mut vector,..}= *this;
///         vector.push(2002);
///     }
///
///     unsafe fn post_drop(this: *mut Self) {
///         let Self{ref mut vector,..}= *this;
///         vector.push(2003);
///     }
/// }
///
///
/// impl Drop for WithDropLogic<'_>{
///     fn drop(&mut self){
///         self.vector.push(1000);
///     }
/// }
///
/// ```
///
pub unsafe trait PrePostDropFields {
    /// Code that runs before the non-moved-out fields are dropped.
    ///
    /// # Safety
    ///
    /// This must only be called right before the `MovedOutFields::drop_fields` implementation,
    /// or inside of `drop_fields` as the first thing that runs in the function.
    #[inline(always)]
    unsafe fn pre_drop(_this: *mut Self) {}

    /// Code that runs after all fields have been dropped.
    ///
    /// # Safety
    ///
    /// This must only be called right after the `MovedOutFields::drop_fields` implementation,
    /// or inside of `drop_fields` as the last thing that runs in the function
    /// (that includes destructors).
    #[inline(always)]
    unsafe fn post_drop(_this: *mut Self) {}
}

/////////////////////////////////////////////////////////////////////////////////

/// Defines how the type is dropped,
/// after some fields were moved out
/// with the [`IntoField::move_out_field_`] or [`IntoVariantField::move_out_vfield_`] methods.
///
/// # Safety
///
/// Implementors must drop the fields that were not moved out.
///
/// To check whether a field with an `Into*Field` implementation was moved out
/// you must call `moved.is_moved_out()`,
/// and pass the same `FieldBit` argument that was used to mark the fields as moved out
/// in its `Into*Field` implementation.
/// If `is_moved_out` returns false, then you must drop the field.
///
/// # Before move method
///
/// You can define drop logic similar to `Drop::drop` with `pre_move`.
///
/// All fields that have accessor impls must still be usable after `pre_move` is called.
///
/// # Example
///
/// For an example of implementing this trait you can look at:
///
/// - for structs:
/// [The manual implementation example](../trait.IntoField.html#manual-implementation-example)
/// for [`IntoField`].
///
/// - for enums:
/// [The manual implementation example
/// ](../trait.IntoVariantField.html#manual-implementation-example)
/// for [`IntoVariantField`].
///
///
/// [`IntoField::move_out_field_`]: ../trait.IntoField.html#tymethod.move_out_field_
///
/// [`IntoVariantField::move_out_vfield_`]:
/// ../trait.IntoVariantField.html#tymethod.move_out_vfield_
///
/// [`IntoField`]: ../trait.IntoField.html
///
/// [`IntoVariantField`]: ../trait.IntoVariantField.html
pub unsafe trait DropFields {
    /// What this type does right before any field is moved.
    fn pre_move(&mut self);

    /// Drops all the fields that were not moved out.
    ///
    /// # Safety
    ///
    /// The passed in `MovedOutFields` must be the same one that was passed by
    /// mutable reference to the
    /// `move_out_field` and/or `move_out_vfield` methods on self,
    /// it must not have been mutated outside those method calls.
    ///
    unsafe fn drop_fields(&mut self, moved: MovedOutFields);
}

/////////////////////////////////////////////////////////////////////////////////

/// Which fields have been moved out of a value.
///
/// This is used to implement converting a value into multiple fields by value.
///
/// # Example
///
/// This is a toy example,
/// for more realistic examples,you can look
/// at
/// [the manual implementation example of `IntoField`
/// ](../trait.IntoField.html#manual-implementation-example)
/// ,or
/// [the manual implementation example of `IntoVariantField`
/// ](../trait.IntoVariantField.html#manual-implementation-example).
///
/// ```rust
/// use structural::field::{MovedOutFields, FieldBit};
///
/// // This is how `FieldBit` is constructed in the derive macro.
/// const FIELD_A_BIT: FieldBit= FieldBit::new(0);
/// const FIELD_B_BIT: FieldBit= FieldBit::new(1);
///
/// let mut moved = MovedOutFields::new();
///
/// // A `MovedOutFields` considers no field moved when constructed.
/// assert!( !moved.is_moved_out(FIELD_A_BIT) );
/// assert!( !moved.is_moved_out(FIELD_B_BIT) );
///
/// // `set_moved_out` is called in the
/// // `IntoField::move_out_field_`/`IntoVariantField::move_out_vfield_` implementation.
/// moved.set_moved_out(FIELD_A_BIT);
/// moved.set_moved_out(FIELD_B_BIT);
///
/// // `is_moved_out` is called in the `DropFields::drop_fields` implementation,
/// //
/// // If `is_moved_out` returns false, then a field has not been moved out,
/// // and must be dropped.
/// assert!( moved.is_moved_out(FIELD_A_BIT) );
/// assert!( moved.is_moved_out(FIELD_B_BIT) );
///
/// ```
#[derive(Debug, Copy, Clone)]
pub struct MovedOutFields(u64);

impl MovedOutFields {
    /// Constructs a MovedOutFields where no field is considered moved out yet.
    #[inline(always)]
    pub const fn new() -> Self {
        MovedOutFields(0)
    }

    /// Marks a field as being moved out.
    #[inline(always)]
    pub fn set_moved_out(&mut self, bit: FieldBit) {
        #[cfg(feature = "testing")]
        let prev = self.0;

        self.0 |= bit.0;

        #[cfg(feature = "testing")]
        assert_ne!(prev, self.0);
    }

    /// Checks whether a field has been moved out.
    #[inline(always)]
    pub const fn is_moved_out(&self, bit: FieldBit) -> bool {
        (self.0 & bit.0) != 0
    }
}

/////////////////////////////////////////////////////////////////////////////////

/// Represents the index for a field in [`MovedOutFields`].
///
/// [`MovedOutFields`]: ./struct.MovedOutFields.html
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct FieldBit(u64);

impl FieldBit {
    /// Constructs a `FieldBit` for the field with its `index`.
    ///
    /// # Panics
    ///
    /// This panics if `index >= 64`
    ///
    /// ```compile_fail
    /// use structural::field::FieldBit;
    ///
    /// const _: FieldBit = FieldBit::new(64);
    ///
    /// ```
    ///
    #[inline(always)]
    pub const fn new(index: u8) -> Self {
        // The effect is that it panics if `index >= 64`
        #[allow(clippy::no_effect)]
        [(); 64][index as usize];
        FieldBit(1 << index)
    }
}