flense 0.1.0

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

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

/// Reified lens.
pub struct Lens<'a, T>
where
    T: TupleSet,
{
    storage: T::ConsPtr,
    _phantom: PhantomData<&'a ()>,
}

// Manual Clone/Copy: the derive would add `T: Clone + Copy` bounds even
// though `T` is purely a phantom type-set tag.
impl<T> Clone for Lens<'_, T>
where
    T: TupleSet,
    T::ConsPtr: Clone,
{
    #[inline]
    fn clone(&self) -> Self {
        Self {
            storage: self.storage.clone(),
            _phantom: PhantomData,
        }
    }
}
impl<T> Copy for Lens<'_, T>
where
    T: TupleSet,
    T::ConsPtr: Copy,
{
}

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

    /// Split `self` into two 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]
    pub fn split<Lhs, Indices>(
        self,
    ) -> (
        Lens<'a, Lhs>,
        Lens<'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();
        (
            Lens {
                storage: lhs_storage,
                _phantom: PhantomData,
            },
            Lens {
                storage: rem_storage,
                _phantom: PhantomData,
            },
        )
    }

    /// Returns a pointer to the first lensed element of a field.
    #[inline]
    pub fn as_ptr<Elt, Index>(&self) -> *const Elt::Type
    where
        Elt: Field + 'static,
        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) -> &'a Elt::Type
    where
        Elt: Field + 'static,
        T::ConsPtr: GetPtr<Elt, Index>,
    {
        // SAFETY: `Adapter` guarantees the stored pointer is valid for reads
        // of `Elt::Type` and properly aligned. The lens borrows the source
        // for `'a` shared, so handing out `&'a Elt::Type` aliases nothing it
        // doesn't already alias.
        unsafe { &*self.storage.get_ptr().as_ptr() }
    }
}