Skip to content

Commit ae6bdf8

Browse files
committed
Add type parameters to generated lenses of generic structs.
This gives type inference something to grab hold of when chaining LensExt methods or using WidgetExt::lens.
1 parent f2aa6f0 commit ae6bdf8

File tree

3 files changed

+76
-11
lines changed

3 files changed

+76
-11
lines changed

druid-derive/src/lens.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ fn derive_struct(input: &syn::DeriveInput) -> Result<proc_macro2::TokenStream, s
6262
"Lens implementations can only be derived from CamelCase types",
6363
));
6464
};
65+
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
66+
67+
let mut lens_ty_idents = Vec::new();
68+
let mut phantom_decls = Vec::new();
69+
let mut phantom_inits = Vec::new();
70+
71+
for gp in input.generics.params.iter() {
72+
if let GenericParam::Type(TypeParam { ident, .. }) = gp {
73+
lens_ty_idents.push(quote! {#ident});
74+
phantom_decls.push(quote! {std::marker::PhantomData<*const #ident>});
75+
phantom_inits.push(quote! {std::marker::PhantomData});
76+
}
77+
}
78+
79+
let lens_ty_generics = quote! {
80+
<#(#lens_ty_idents),*>
81+
};
6582

6683
// Define lens types for each field
6784
let defs = fields.iter().filter(|f| !f.attrs.ignore).map(|f| {
@@ -70,14 +87,20 @@ fn derive_struct(input: &syn::DeriveInput) -> Result<proc_macro2::TokenStream, s
7087
"Lens for the field `{}` on [`{1}`](super::{1})",
7188
field_name, ty
7289
);
90+
7391
quote! {
7492
#[doc = #docs]
7593
#[allow(non_camel_case_types)]
7694
#[derive(Debug, Copy, Clone)]
77-
pub struct #field_name;
95+
pub struct #field_name#lens_ty_generics(#(#phantom_decls),*);
96+
97+
impl #lens_ty_generics #field_name#lens_ty_generics{
98+
pub const fn new()->Self{
99+
Self(#(#phantom_inits),*)
100+
}
101+
}
78102
}
79103
});
80-
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
81104

82105
let used_params: HashSet<String> = input
83106
.generics
@@ -107,7 +130,8 @@ fn derive_struct(input: &syn::DeriveInput) -> Result<proc_macro2::TokenStream, s
107130
let field_ty = &f.ty;
108131

109132
quote! {
110-
impl #impl_generics druid::Lens<#ty#ty_generics, #field_ty> for #twizzled_name::#field_name #where_clause {
133+
134+
impl #impl_generics druid::Lens<#ty#ty_generics, #field_ty> for #twizzled_name::#field_name#lens_ty_generics #where_clause {
111135
fn with<#val_ty_par, #func_ty_par: FnOnce(&#field_ty) -> #val_ty_par>(&self, data: &#ty#ty_generics, f: #func_ty_par) -> #val_ty_par {
112136
f(&data.#field_name)
113137
}
@@ -125,7 +149,7 @@ fn derive_struct(input: &syn::DeriveInput) -> Result<proc_macro2::TokenStream, s
125149

126150
quote! {
127151
/// Lens for the corresponding field
128-
pub const #lens_field_name: #twizzled_name::#field_name = #twizzled_name::#field_name;
152+
pub const #lens_field_name: #twizzled_name::#field_name#lens_ty_generics = #twizzled_name::#field_name::new();
129153
}
130154
});
131155

druid-derive/tests/lens_generic.rs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use druid::Lens;
1+
use druid::{Lens, LensExt};
22
use std::fmt::Debug;
33
use std::marker::PhantomData;
44

@@ -108,7 +108,8 @@ fn where_clause() {
108108

109109
#[derive(Lens)]
110110
struct ReservedParams<F, V> {
111-
f: F, // We were using V and F as method params
111+
f: F,
112+
// We were using V and F as method params
112113
v: V,
113114
}
114115

@@ -121,3 +122,46 @@ fn reserved() {
121122
let val = ReservedParams::<u64, String>::f.with(&rp, |val| *val);
122123
assert_eq!(rp.f, val);
123124
}
125+
126+
#[derive(Lens)]
127+
struct Outer<T> {
128+
middle: Middle,
129+
t: T,
130+
}
131+
132+
#[derive(Lens)]
133+
struct Middle {
134+
internal: usize,
135+
}
136+
137+
#[test]
138+
fn then_inference() {
139+
let outer = Outer {
140+
t: -9i32,
141+
middle: Middle { internal: 89 },
142+
};
143+
144+
let lens = Outer::<i32>::middle.then(Middle::internal);
145+
let val = lens.with(&outer, |val| *val);
146+
assert_eq!(outer.middle.internal, val);
147+
148+
let outer = Outer {
149+
t: Middle { internal: 12 },
150+
middle: Middle { internal: 567 },
151+
};
152+
153+
let lens = Outer::<Middle>::t.then(Middle::internal);
154+
let val = lens.with(&outer, |val| *val);
155+
assert_eq!(outer.t.internal, val);
156+
157+
let lt_wrapper = LifetimeWrapper {
158+
x: Middle { internal: 45 },
159+
phantom_a: Default::default(),
160+
};
161+
162+
let lens = LifetimeWrapper::<'static, Middle>::x.then(Middle::internal);
163+
let val = lens.with(&lt_wrapper, |val| *val);
164+
assert_eq!(lt_wrapper.x.internal, val);
165+
166+
//let outer = Outer
167+
}

druid/src/widget/tabs.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -279,10 +279,7 @@ impl<TP: TabsPolicy> TabBar<TP> {
279279
let label = data
280280
.policy
281281
.tab_label(key.clone(), info, &data.inner)
282-
// TODO: Type inference fails here because both sides of the lens are dependent on
283-
// associated types of the policy. Needs changes to lens derivation to embed PhantomData of the (relevant?) type params)
284-
// of the lensed types into the lens, so type inference has something to grab hold of
285-
.lens::<TabsState<TP>, tabs_state_derived_lenses::inner>(TabsState::<TP>::inner)
282+
.lens(TabsState::<TP>::inner)
286283
.padding(Insets::uniform_xy(9., 5.));
287284

288285
if can_close {
@@ -718,7 +715,7 @@ impl<TP> TabsScopePolicy<TP> {
718715
impl<TP: TabsPolicy> ScopePolicy for TabsScopePolicy<TP> {
719716
type In = TP::Input;
720717
type State = TabsState<TP>;
721-
type Transfer = LensScopeTransfer<tabs_state_derived_lenses::inner, Self::In, Self::State>;
718+
type Transfer = LensScopeTransfer<tabs_state_derived_lenses::inner<TP>, Self::In, Self::State>;
722719

723720
fn create(self, inner: &Self::In) -> (Self::State, Self::Transfer) {
724721
(

0 commit comments

Comments
 (0)