From cc9c90202b71b9ce0265729c0767b6d74d4d89be Mon Sep 17 00:00:00 2001 From: annieversary Date: Wed, 17 Nov 2021 16:02:07 +0000 Subject: [PATCH] implement with_ref stuff, so you can use field lenses with a struct and a ref --- bad-optics-derive/src/lib.rs | 30 +++++++++++-- examples/derive.rs | 7 ++- src/lenses/fields.rs | 14 ++++++ src/lenses/lens.rs | 2 +- src/lenses/lens_with_ref.rs | 83 ++++++++++++++++++++++++++++++++++++ src/lenses/mod.rs | 6 ++- src/lenses/to.rs | 22 +++++++++- 7 files changed, 154 insertions(+), 10 deletions(-) create mode 100644 src/lenses/lens_with_ref.rs diff --git a/bad-optics-derive/src/lib.rs b/bad-optics-derive/src/lib.rs index 2066d38..16e86f5 100644 --- a/bad-optics-derive/src/lib.rs +++ b/bad-optics-derive/src/lib.rs @@ -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< + 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::(); @@ -79,7 +89,19 @@ fn expand_struct(data: DataStruct, name: &Ident, mod_name: &Ident) -> TokenStrea quote! { impl Lenses<#ty> { pub fn get() -> - Vec>> + 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 diff --git a/examples/derive.rs b/examples/derive.rs index b9df645..1e86a8b 100644 --- a/examples/derive.rs +++ b/examples/derive.rs @@ -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::::get(); diff --git a/src/lenses/fields.rs b/src/lenses/fields.rs index de6c16b..2a3b0cd 100644 --- a/src/lenses/fields.rs +++ b/src/lenses/fields.rs @@ -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(), + ) + }; +} diff --git a/src/lenses/lens.rs b/src/lenses/lens.rs index a9f57f7..c57ab22 100644 --- a/src/lenses/lens.rs +++ b/src/lenses/lens.rs @@ -30,7 +30,7 @@ impl LensOver for FuncLens { } /// Makes a lens that implements `LensView` and `LensOver` with the provided functions -pub fn lens_from_boxed( +pub fn lens_from_arc( getter: Arc>, setter: Arc>, ) -> Lens> { diff --git a/src/lenses/lens_with_ref.rs b/src/lenses/lens_with_ref.rs new file mode 100644 index 0000000..c39c763 --- /dev/null +++ b/src/lenses/lens_with_ref.rs @@ -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, std::marker::PhantomData); + +impl<'a, A, B, T> LensView for LensWithRef, B, T> +where + T: Clone, + A: LensView, +{ + type Field = A::Field; + + fn view(&self, thing: T) -> Self::Field { + A::view(&self.0 .0, thing) + } +} +impl<'a, A, B, T> LensOver for LensWithRef, B, T> +where + T: Clone, + A: LensOver, +{ + fn over(&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, T> +where + T: Clone, + A: LensView, + B: LensView<&'a T>, +{ + type Field = B::Field; + + fn view(&self, thing: &'a T) -> Self::Field { + B::view(&self.1 .0, thing) + } +} + +type Getter = dyn Fn(T) -> U; +type Setter = dyn Fn(T, U) -> T; + +/// Makes a lens that implements `LensView` and `LensOver` with the provided functions +pub fn lens_with_ref_from_arc( + getter: Arc>, + setter: Arc>, + getter_ref: Arc U>, +) -> Lens>, Lens>, 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` and `LensOver` with the provided functions +pub fn lens_with_ref( + getter: impl Fn(T) -> U + 'static, + setter: impl Fn(T, U) -> T + 'static, + getter_ref: impl Fn(&T) -> U + 'static, +) -> Lens>, Lens>, T>> +where + T: Clone, +{ + Lens(LensWithRef( + lens(getter, setter), + to_ref(getter_ref), + Default::default(), + )) +} diff --git a/src/lenses/mod.rs b/src/lenses/mod.rs index 6d4bfc6..42742b5 100644 --- a/src/lenses/mod.rs +++ b/src/lenses/mod.rs @@ -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)] diff --git a/src/lenses/to.rs b/src/lenses/to.rs index 36c1af2..ac88ba2 100644 --- a/src/lenses/to.rs +++ b/src/lenses/to.rs @@ -16,7 +16,7 @@ impl LensView for ToInner { } /// Makes a lens that implements `LensView` with the provided function -pub fn to_from_boxed(f: Arc U>) -> Lens> { +pub fn to_from_arc(f: Arc U>) -> Lens> { Lens(ToInner(f)) } /// Makes a lens that implements `LensView` with the provided function @@ -30,3 +30,23 @@ impl Lens> { Lens(FuncLens((self.0).0, Arc::new(setter))) } } + +#[derive(Clone)] +pub struct ToRefInner(Arc U>); + +impl LensView<&T> for ToRefInner { + type Field = U; + + fn view(&self, thing: &T) -> Self::Field { + (self.0)(thing) + } +} + +/// Makes a lens that implements `LensView` with the provided function +pub fn to_ref_from_arc(f: Arc U>) -> Lens> { + Lens(ToRefInner(f)) +} +/// Makes a lens that implements `LensView` with the provided function +pub fn to_ref(f: impl Fn(&T) -> U + 'static) -> Lens> { + Lens(ToRefInner(Arc::new(f))) +}