flense 0.2.0

Purpose-oriented lensing
Documentation
use core::marker::PhantomData;

use crate::{
    Field,
    lenses::LensesMut,
    type_lists::{
        Concat,
        ConsSet,
        GetPtr,
        SculptPtr,
        TupleSet,
    },
};

/// Mutable reified lens.
///
/// Refers to a mutable set of [`Field`].
///
/// This is to lensing what a `&mut` reference is to references.
#[derive(Debug)]
pub struct LensMut<'a, T>
where
    T: TupleSet,
{
    storage: T::ConsPtr,
    _phantom: PhantomData<&'a mut ()>,
}

// The lack of Copy + Clone for LensMut is for correctness; ConsPtr is always
// Copy + Clone since it is just pointers, but allowing a copy of the
// reference-like LensMut would be a soundness issue.

impl<'a, T> LensesMut<'a, T> for LensMut<'a, T>
where
    T: TupleSet,
{
    #[inline]
    fn lens_mut(self) -> Self {
        self
    }
}

impl<'a, T> LensMut<'a, T>
where
    T: TupleSet,
{
    #[inline]
    #[must_use]
    pub(crate) const fn new(storage: T::ConsPtr) -> Self {
        Self {
            storage,
            _phantom: PhantomData,
        }
    }

    /// Split `self` into two mutable lenses.
    ///
    /// `Lhs` is the tuple of fields that should appear in the left-hand
    /// returned lens; the right-hand lens contains the remaining fields.
    ///
    /// `Indices` is a single tuple-nested list of shrinking-list positions (one
    /// per element of `Lhs`) and should be inferred; pass `_` at the call site.
    #[expect(clippy::type_complexity, reason = "necessary type inference")]
    #[inline]
    #[must_use]
    pub fn split<Lhs, Indices>(
        self,
    ) -> (
        LensMut<'a, Lhs>,
        LensMut<
            'a,
            <<T::ConsPtr as SculptPtr<Lhs::ConsPtr, Indices>>::Remainder as ConsSet>::Tuple,
        >,
    )
    where
        Lhs: TupleSet,
        T::ConsPtr: SculptPtr<Lhs::ConsPtr, Indices>,
        <T::ConsPtr as SculptPtr<Lhs::ConsPtr, Indices>>::Remainder: ConsSet<
            Tuple: TupleSet<ConsPtr = <T::ConsPtr as SculptPtr<Lhs::ConsPtr, Indices>>::Remainder>,
        >,
    {
        let (lhs_storage, rem_storage) = self.storage.sculpt_ptr();
        (
            LensMut {
                storage: lhs_storage,
                _phantom: PhantomData,
            },
            LensMut {
                storage: rem_storage,
                _phantom: PhantomData,
            },
        )
    }

    /// Joins another [`LensMut`] into `self`, forming one.
    #[expect(clippy::type_complexity, reason = "necessary type inference")]
    #[inline]
    #[must_use]
    pub fn join<Rhs>(
        self,
        other: LensMut<'a, Rhs>,
    ) -> LensMut<'a, <<T::ConsPtr as Concat<T::ConsPtr, Rhs::ConsPtr>>::Result as ConsSet>::Tuple>
    where
        Rhs: TupleSet,
        T::ConsPtr: Concat<T::ConsPtr, Rhs::ConsPtr>,
        <T::ConsPtr as Concat<T::ConsPtr, Rhs::ConsPtr>>::Result: ConsSet<
            Tuple: TupleSet<ConsPtr = <T::ConsPtr as Concat<T::ConsPtr, Rhs::ConsPtr>>::Result>,
        >,
    {
        LensMut {
            storage: self.storage.concat(other.storage),
            _phantom: PhantomData,
        }
    }

    /// Returns a pointer to the first lensed element of a field.
    #[inline]
    #[must_use]
    pub fn as_ptr<Elt, Index>(&self) -> *const Elt::Type
    where
        Elt: Field,
        T::ConsPtr: GetPtr<Elt, Index>,
    {
        self.storage.get_ptr().as_ptr()
    }

    /// Returns a mutable pointer to the first lensed element of a field.
    #[inline]
    #[must_use]
    pub fn as_mut_ptr<Elt, Index>(&mut self) -> *mut Elt::Type
    where
        Elt: Field,
        T::ConsPtr: GetPtr<Elt, Index>,
    {
        self.storage.get_ptr().as_ptr()
    }

    /// Returns a shared reference to the lensed field.
    #[expect(
        clippy::should_implement_trait,
        reason = "signature is generic over the field type, not interchangeable with `AsRef`"
    )]
    #[inline]
    #[must_use]
    pub fn as_ref<Elt, Index>(&self) -> &Elt::Type
    where
        Elt: Field,
        T::ConsPtr: GetPtr<Elt, Index>,
    {
        // SAFETY: `Adapter` guarantees the stored pointer is valid for reads
        // of `Elt::Type` and properly aligned. Tying the returned reference
        // to `&self` reborrows the lens shared for the call's lifetime, so
        // no `&mut Elt::Type` can coexist.
        unsafe { &*self.storage.get_ptr().as_ptr() }
    }

    /// Returns a unique reference to the lensed field.
    #[expect(
        clippy::should_implement_trait,
        reason = "signature is generic over the field type, not interchangeable with `AsMut`"
    )]
    #[inline]
    #[must_use]
    pub fn as_mut<Elt, Index>(&mut self) -> &mut Elt::Type
    where
        Elt: Field,
        T::ConsPtr: GetPtr<Elt, Index>,
    {
        // SAFETY: `Adapter` guarantees the stored pointer is valid for reads
        // and writes of `Elt::Type` and properly aligned. `&mut self`
        // reborrows the lens uniquely for the call's lifetime, so the
        // returned `&mut` cannot alias any other reference reachable through
        // the lens.
        unsafe { &mut *self.storage.get_ptr().as_ptr() }
    }
}