Skip to content

Commit

Permalink
Expose derived lenses as associated constants
Browse files Browse the repository at this point in the history
  • Loading branch information
Ralith authored and cmyr committed Nov 25, 2019
1 parent b0a6e2d commit 5b54d7f
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 22 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ struct AppState {
### lens

The [Lens datatype] gives access to a part of a larger data structure. Like
`Data`, This can be derived.
`Data`, this can be derived. Derived lenses are accessed as associated constants
with the same name as the field.

```rust
#[derive(Clone, Data, Lens)]
Expand All @@ -202,7 +203,7 @@ To use the lens, wrap your widget with `LensWrap` (note the conversion of
CamelCase to snake_case):

```rust
LensWrap::new(WidgetThatExpectsf64::new(), app_state::value);
LensWrap::new(WidgetThatExpectsf64::new(), AppState::value);
```

## Using druid
Expand Down
32 changes: 19 additions & 13 deletions druid-derive/src/lens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fn derive_struct(input: &syn::DeriveInput) -> Result<proc_macro2::TokenStream, s
};

let twizzled_name = if is_camel_case(&ty.to_string()) {
let temp_name = to_snake_case(&ty.to_string());
let temp_name = format!("{}_derived_lenses", to_snake_case(&ty.to_string()));
proc_macro2::Ident::new(&temp_name, proc_macro2::Span::call_site())
} else {
return Err(syn::Error::new(
Expand All @@ -57,22 +57,15 @@ fn derive_struct(input: &syn::DeriveInput) -> Result<proc_macro2::TokenStream, s
));
};

// Declare a struct for each field
let structs = fields.iter().map(|f| {
let field_name = &f.ident;

quote! {
#[allow(non_camel_case_types)]
pub struct #field_name;
}
});

// Impl Lens for each field
let impls = fields.iter().map(|f| {
let field_name = &f.ident;
let field_ty = &f.ty;

quote! {
/// Lens for the field on #ty
#[allow(non_camel_case_types)]
pub struct #field_name;

impl Lens<#ty, #field_ty> for #field_name {
fn with<V, F: FnOnce(&#field_ty) -> V>(&self, data: &#ty, f: F) -> V {
Expand All @@ -86,13 +79,26 @@ fn derive_struct(input: &syn::DeriveInput) -> Result<proc_macro2::TokenStream, s
}
});

let associated_items = fields.iter().map(|f| {
let field_name = &f.ident;
quote! {
/// Lens for the corresponding field
pub const #field_name: #field_name = #field_name;
}
});

let expanded = quote! {
pub mod #twizzled_name {
//! Lens definitions for #ty
use super::*;

use druid::Lens;
#(#structs)*

#(#impls)*

#[allow(non_upper_case_globals)]
impl #ty {
#(#associated_items)*
}
}
};

Expand Down
4 changes: 4 additions & 0 deletions druid-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ pub fn derive_data(input: TokenStream) -> TokenStream {
.into()
}

/// Generates lenses to access the fields of a struct
///
/// An associated constant is defined on the struct for each field,
/// having the same name as the field.
#[proc_macro_derive(Lens)]
pub fn derive_lens(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as syn::DeriveInput);
Expand Down
2 changes: 1 addition & 1 deletion druid/examples/calc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ fn build_calc() -> impl Widget<CalcState> {
let mut column = Flex::column();
let display = LensWrap::new(
DynLabel::new(|data: &String, _env| data.clone()),
calc_state::value,
CalcState::value,
);
column.add_child(pad(display), 0.0);
column.add_child(
Expand Down
4 changes: 2 additions & 2 deletions druid/examples/either.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ fn ui_builder() -> impl Widget<AppState> {

let mut col = Flex::column();
col.add_child(
Padding::new(5.0, LensWrap::new(Checkbox::new(), app_state::which)),
Padding::new(5.0, LensWrap::new(Checkbox::new(), AppState::which)),
0.0,
);
let either = Either::new(
|data, _env| data.which,
Padding::new(5.0, LensWrap::new(Slider::new(), app_state::value)),
Padding::new(5.0, LensWrap::new(Slider::new(), AppState::value)),
Padding::new(5.0, label),
);
col.add_child(either, 0.0);
Expand Down
6 changes: 3 additions & 3 deletions druid/examples/slider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ fn build_widget() -> impl Widget<DemoState> {
}
});
let mut row = Flex::row();
let checkbox = LensWrap::new(Checkbox::new(), demo_state::double);
let checkbox = LensWrap::new(Checkbox::new(), DemoState::double);
let checkbox_label = Label::new("double the value");
row.add_child(checkbox, 0.0);
row.add_child(Padding::new(5.0, checkbox_label), 1.0);

let bar = LensWrap::new(ProgressBar::new(), demo_state::value);
let slider = LensWrap::new(Slider::new(), demo_state::value);
let bar = LensWrap::new(ProgressBar::new(), DemoState::value);
let slider = LensWrap::new(Slider::new(), DemoState::value);

let button_1 = Button::sized(
"increment ",
Expand Down
2 changes: 1 addition & 1 deletion druid/examples/switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct DemoState {
fn build_widget() -> impl Widget<DemoState> {
let mut col = Flex::column();
let mut row = Flex::row();
let switch = LensWrap::new(Switch::new(), demo_state::value);
let switch = LensWrap::new(Switch::new(), DemoState::value);
let switch_label = Label::new("Setting label");

row.add_child(Padding::new(5.0, switch_label), 0.0);
Expand Down

0 comments on commit 5b54d7f

Please sign in to comment.