implement with_ref stuff, so you can use field lenses with a struct and a ref

This commit is contained in:
annieversary 2021-11-17 16:02:07 +00:00
parent 4a1f398fe3
commit cc9c90202b
7 changed files with 154 additions and 10 deletions

View file

@ -55,9 +55,19 @@ fn expand_struct(data: DataStruct, name: &Ident, mod_name: &Ident) -> TokenStrea
let ty = &field.ty;
quote! {
pub fn #fname() ->
bad_optics::lenses::Lens<bad_optics::lenses::lens::FuncLens<#name, #ty>>
bad_optics::lenses::Lens<
bad_optics::lenses::lens_with_ref::LensWithRef<
bad_optics::lenses::Lens<
bad_optics::lenses::lens::FuncLens<#name, #ty>
>,
bad_optics::lenses::Lens<
bad_optics::lenses::to::ToRefInner<#name, #ty>
>,
#name
>
>
{
bad_optics::field_lens!(#name, #fname)
bad_optics::field_lens_with_ref!(#name, #fname)
}
}
})
@ -71,7 +81,7 @@ fn expand_struct(data: DataStruct, name: &Ident, mod_name: &Ident) -> TokenStrea
.map(|field| {
let fname = field.ident.unwrap();
quote! {
bad_optics::field_lens!(#name, #fname),
bad_optics::field_lens_with_ref!(#name, #fname),
}
})
.collect::<TokenStream>();
@ -79,7 +89,19 @@ fn expand_struct(data: DataStruct, name: &Ident, mod_name: &Ident) -> TokenStrea
quote! {
impl Lenses<#ty> {
pub fn get() ->
Vec<bad_optics::lenses::Lens<bad_optics::lenses::lens::FuncLens<#name, #ty>>>
Vec<
bad_optics::lenses::Lens<
bad_optics::lenses::lens_with_ref::LensWithRef<
bad_optics::lenses::Lens<
bad_optics::lenses::lens::FuncLens<#name, #ty>
>,
bad_optics::lenses::Lens<
bad_optics::lenses::to::ToRefInner<#name, #ty>
>,
#name
>
>
>
{
vec![
#lenses

View file

@ -23,12 +23,15 @@ fn main() {
// we can manually get lenses for each field
// note that it's a function that returns a lens
//
// these lenses work for `MyStruct` with `view` and `over`,
// and for `&MyStruct` with `view`
let field1 = mystruct::field1();
let field2 = mystruct::field2();
// the lenses work normally as any other lens :)
assert_eq!(field1(o.clone()), "first field");
assert_eq!(field2(o.clone()), "second field");
assert_eq!(field1(&o), "first field");
assert_eq!(field2(&o), "second field");
// we can get a vec with all the lenses that match a type
let string_lenses = mystruct::Lenses::<String>::get();

View file

@ -10,3 +10,17 @@ macro_rules! field_lens {
)
};
}
#[macro_export]
macro_rules! field_lens_with_ref {
($type:ident, $field:ident) => {
$crate::lenses::lens_with_ref(
|v: $type| v.$field,
|mut u: $type, v| {
u.$field = v;
u
},
|v: &$type| v.$field.clone(),
)
};
}

View file

@ -30,7 +30,7 @@ impl<T: Clone, U> LensOver<T> for FuncLens<T, U> {
}
/// Makes a lens that implements `LensView<T>` and `LensOver<T>` with the provided functions
pub fn lens_from_boxed<T, U>(
pub fn lens_from_arc<T, U>(
getter: Arc<Getter<T, U>>,
setter: Arc<Setter<T, U>>,
) -> Lens<FuncLens<T, U>> {

View file

@ -0,0 +1,83 @@
use crate::lenses::{
lens,
lens::FuncLens,
lens_from_arc,
to::{to_ref, to_ref_from_arc, ToRefInner},
Lens, LensOver, LensView,
};
use std::sync::Arc;
#[derive(Clone, Copy)]
/// Lens that works on both a T and &T
pub struct LensWithRef<A, B, T>(A, B, std::marker::PhantomData<T>);
impl<'a, A, B, T> LensView<T> for LensWithRef<Lens<A>, B, T>
where
T: Clone,
A: LensView<T>,
{
type Field = A::Field;
fn view(&self, thing: T) -> Self::Field {
A::view(&self.0 .0, thing)
}
}
impl<'a, A, B, T> LensOver<T> for LensWithRef<Lens<A>, B, T>
where
T: Clone,
A: LensOver<T>,
{
fn over<F>(&self, thing: T, f: F) -> T
where
F: FnOnce(Self::Field) -> Self::Field,
{
A::over(&self.0 .0, thing, f)
}
}
impl<'a, A, B, T> LensView<&'a T> for LensWithRef<A, Lens<B>, T>
where
T: Clone,
A: LensView<T>,
B: LensView<&'a T>,
{
type Field = B::Field;
fn view(&self, thing: &'a T) -> Self::Field {
B::view(&self.1 .0, thing)
}
}
type Getter<T, U> = dyn Fn(T) -> U;
type Setter<T, U> = dyn Fn(T, U) -> T;
/// Makes a lens that implements `LensView<T>` and `LensOver<T>` with the provided functions
pub fn lens_with_ref_from_arc<T, U>(
getter: Arc<Getter<T, U>>,
setter: Arc<Setter<T, U>>,
getter_ref: Arc<dyn Fn(&T) -> U>,
) -> Lens<LensWithRef<Lens<FuncLens<T, U>>, Lens<ToRefInner<T, U>>, T>>
where
T: Clone,
{
Lens(LensWithRef(
lens_from_arc(getter, setter),
to_ref_from_arc(getter_ref),
Default::default(),
))
}
/// Makes a lens that implements `LensView<T>` and `LensOver<T>` with the provided functions
pub fn lens_with_ref<T, U>(
getter: impl Fn(T) -> U + 'static,
setter: impl Fn(T, U) -> T + 'static,
getter_ref: impl Fn(&T) -> U + 'static,
) -> Lens<LensWithRef<Lens<FuncLens<T, U>>, Lens<ToRefInner<T, U>>, T>>
where
T: Clone,
{
Lens(LensWithRef(
lens(getter, setter),
to_ref(getter_ref),
Default::default(),
))
}

View file

@ -15,9 +15,11 @@ pub mod fifth;
pub use fifth::_4;
pub mod to;
pub use to::{to, to_from_boxed};
pub use to::{to, to_from_arc};
pub mod lens;
pub use lens::{lens, lens_from_boxed};
pub use lens::{lens, lens_from_arc};
pub mod lens_with_ref;
pub use lens_with_ref::{lens_with_ref, lens_with_ref_from_arc};
/// Wrapper type
#[derive(Clone, Copy)]

View file

@ -16,7 +16,7 @@ impl<T, U> LensView<T> for ToInner<T, U> {
}
/// Makes a lens that implements `LensView<T>` with the provided function
pub fn to_from_boxed<T, U>(f: Arc<dyn Fn(T) -> U>) -> Lens<ToInner<T, U>> {
pub fn to_from_arc<T, U>(f: Arc<dyn Fn(T) -> U>) -> Lens<ToInner<T, U>> {
Lens(ToInner(f))
}
/// Makes a lens that implements `LensView<T>` with the provided function
@ -30,3 +30,23 @@ impl<T, U> Lens<ToInner<T, U>> {
Lens(FuncLens((self.0).0, Arc::new(setter)))
}
}
#[derive(Clone)]
pub struct ToRefInner<T, U>(Arc<dyn Fn(&T) -> U>);
impl<T, U> LensView<&T> for ToRefInner<T, U> {
type Field = U;
fn view(&self, thing: &T) -> Self::Field {
(self.0)(thing)
}
}
/// Makes a lens that implements `LensView<T>` with the provided function
pub fn to_ref_from_arc<T, U>(f: Arc<dyn Fn(&T) -> U>) -> Lens<ToRefInner<T, U>> {
Lens(ToRefInner(f))
}
/// Makes a lens that implements `LensView<T>` with the provided function
pub fn to_ref<T, U>(f: impl Fn(&T) -> U + 'static) -> Lens<ToRefInner<T, U>> {
Lens(ToRefInner(Arc::new(f)))
}