grist_lens 1.2.4

Unsafe Nightly Ergonomic Lens API
Documentation
/// # Usage
/// ```
/// use grist_lens::lens;
///
/// #[lens]
/// struct Foo {
///     x: i32,
///     y: u32,
/// }
/// ```
/// This generates something similar to
/// ```ignore
/// struct Foo {
///     x: i32,
///     y: u32,
/// }
///
/// struct Foo_x;
/// impl Field for Foo_x {
///     const INDEX: usize = 0;
///     const OFFSET: usize = 0;
///
///     type Type = i32;
///     type Base = Foo;
/// }
///
/// struct Foo_y;
/// impl Field for Foo_x {
///     const INDEX: usize = 1;
///     const OFFSET: usize = 4;
///
///     type Type = u32;
///     type Base = Foo;
/// }
/// ```
/// `Foo_x` and `Foo_y` are used as field indexers when getting fields in `Optic`.
pub use grist_lens_macro::lens;

use std::{cell::Cell, marker::PhantomData};

use crate::{Field, Lens, LensMut, LensRef, Struct, Usage};

/// Creates lenses from a struct
pub struct Optic<'a, T: Struct> {
    value: *mut T,
    usages: Box<[Cell<Usage>]>,
    phantom: PhantomData<&'a mut T>,
}

impl<'a, T: Struct> Optic<'a, T> {
    /// Creates a new Optic which can create `LensRef` and `LensMut`.
    /// # Example
    /// ```
    /// use grist_lens::{Optic, lens};
    ///
    /// #[lens]
    /// struct Foo { x: i32, y: u32 };
    ///
    /// let mut object = Foo { x: 10, y: 20 };
    /// let object_optic = Optic::new(&mut object);
    /// ```
    pub fn new(value: &'a mut T) -> Self {
        Self {
            value,
            usages: vec![Cell::new(Usage::None); T::FIELD_COUNT].into(),
            phantom: PhantomData,
        }
    }

    /// Grabs a snapshot of which fields are currently being used, indexed by their field number.
    /// # Example
    /// ```
    /// use grist_lens::{Optic, lens, Usage, Field};
    ///
    /// #[lens]
    /// #[repr(C)]
    /// struct Foo { x: i32, y: u32 };
    ///
    /// let mut object = Foo { x: 10, y: 20 };
    /// let object_optic = Optic::new(&mut object);
    ///
    /// let usages = object_optic.usages();
    /// assert_eq!(usages[Foo_x::INDEX], Usage::None);
    /// ```
    pub fn usages(&self) -> Vec<Usage> {
        // SAFETY: Vec<Cell<T>> -> &[T] -> Vec<T> is ok. this creates a clone.
        unsafe { std::slice::from_raw_parts(self.usages.as_ptr().cast(), T::FIELD_COUNT) }.to_vec()
    }

    fn usage<O: Field>(&self) -> &Cell<Usage> {
        &self.usages[O::INDEX]
    }
}

impl<'a, T> Lens<'a, T> for Optic<'a, T>
where
    T: Struct + 'a,
{
    /// Get a `LensRef` from an `Optic`.
    /// # Example
    /// ```
    /// use grist_lens::{Optic, lens, Usage, Field, Lens};
    ///
    /// #[lens]
    /// struct Foo { x: i32, y: u32 };
    ///
    /// let mut object = Foo { x: 10, y: 20 };
    /// let object_optic = Optic::new(&mut object);
    ///
    /// let x_field = object_optic.get::<Foo_x>();
    /// ```
    fn get<'b, O: Field<Base = T>>(&'b self) -> LensRef<'b, O> {
        self.usage::<O>().update(|x| match x {
            Usage::Mut => panic!("gulp"),
            Usage::Ref(x @ 1..) => Usage::Ref(x + 1),
            _ => Usage::Ref(1),
        });

        LensRef {
            usage: self.usage::<O>(),
            // SAFETY: O::OFFSET sourced from offset_of!
            field: unsafe { &*(self.value.cast::<u8>().add(O::OFFSET).cast()) },
            phantom: PhantomData,
        }
    }

    /// Get a `LensMut` from an `Optic`.
    /// # Example
    /// ```
    /// use grist_lens::{Optic, lens, Usage, Field, Lens};
    ///
    /// #[lens]
    /// #[repr(C)]
    /// struct Foo { x: i32, y: u32 };
    ///
    /// let mut object = Foo { x: 10, y: 20 };
    /// let object_optic = Optic::new(&mut object);
    ///
    /// let x_field = object_optic.get_mut::<Foo_x>();
    /// ```
    fn get_mut<'b, O: Field<Base = T>>(&'b self) -> LensMut<'b, O> {
        self.usage::<O>().update(|x| match x {
            Usage::None => Usage::Mut,
            // TODO: Add real error
            _ => panic!("gulp"),
        });

        LensMut {
            usage: self.usage::<O>(),
            // SAFETY: O::OFFSET sourced from offset_of!
            field: unsafe { &mut *(self.value.cast::<u8>().add(O::OFFSET).cast()) },
            phantom: PhantomData,
        }
    }
}