implement with_ref stuff, so you can use field lenses with a struct and a ref
This commit is contained in:
parent
4a1f398fe3
commit
cc9c90202b
7 changed files with 154 additions and 10 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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>> {
|
||||
|
|
83
src/lenses/lens_with_ref.rs
Normal file
83
src/lenses/lens_with_ref.rs
Normal 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(),
|
||||
))
|
||||
}
|
|
@ -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)]
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue