Skip to content

Commit ba6cad2

Browse files
committed
Allow call expressions with multiple args in builder(foreach)
1 parent f2b4da5 commit ba6cad2

2 files changed

Lines changed: 93 additions & 93 deletions

File tree

cursive-core/src/views/dialog.rs

Lines changed: 29 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -932,76 +932,46 @@ impl View for Dialog {
932932
}
933933
}
934934

935-
/*
936-
#[crate::blueprint(Dialog::new())]
937-
struct Blueprint {
938-
title: String,
939-
940-
content: Option<BoxedView>,
941-
942-
// TODO: buttons?
943-
// Define some Button type?
944-
// Implement Resolvable as blueprints?
945-
// Allow blueprint(foreach) with multiple arguments?
946-
// Ex: foreach(add_button_with_cb( .key, .value ))
935+
struct Btn {
936+
key: String,
937+
value: std::sync::Arc<dyn Fn(&mut Cursive) + Send + Sync>,
947938
}
948-
*/
949939

950-
crate::manual_blueprint!(Dialog, |config, context| {
951-
use crate::builder::{Config, Context, Error, Resolvable};
952-
let mut dialog = Dialog::new();
953-
954-
if let Some(title) = context.resolve(&config["title"])? {
955-
dialog.set_title::<StyledString>(title);
956-
}
940+
#[cfg(feature = "builder")]
941+
use crate::builder::{Config, Context, Error, Resolvable};
957942

958-
if let Some(title_position) = context.resolve(&config["title_position"])? {
959-
dialog.set_title_position(title_position);
960-
}
943+
#[cfg(feature = "builder")]
944+
impl Resolvable for Btn {
945+
fn from_config(config: &Config, context: &Context) -> Result<Self, Error> {
946+
let config = config
947+
.as_object()
948+
.ok_or_else(|| Error::invalid_config("Expected object", config))?;
961949

962-
let content: Option<BoxedView> = context.resolve(&config["content"])?;
963-
if let Some(content) = content {
964-
dialog.set_content(content);
965-
}
950+
let (key, value) = config
951+
.iter()
952+
.next()
953+
.ok_or_else(|| Error::invalid_config("Expected non-empty object", config))?;
966954

967-
if let Some(padding) = context.resolve(&config["padding"])? {
968-
dialog.set_padding(padding);
969-
}
955+
let key = key.into();
956+
let value = context.resolve(value)?;
970957

971-
struct Btn {
972-
key: String,
973-
value: std::sync::Arc<dyn Fn(&mut Cursive) + Send + Sync>,
958+
Ok(Btn { key, value })
974959
}
960+
}
975961

976-
impl Resolvable for Btn {
977-
fn from_config(config: &Config, context: &Context) -> Result<Self, Error> {
978-
let config = config
979-
.as_object()
980-
.ok_or_else(|| Error::invalid_config("Expected object", config))?;
981-
982-
let (key, value) = config
983-
.iter()
984-
.next()
985-
.ok_or_else(|| Error::invalid_config("Expected non-empty object", config))?;
986-
987-
let key = key.into();
988-
let value = context.resolve(value)?;
989-
990-
Ok(Btn { key, value })
991-
}
992-
}
962+
#[crate::blueprint(Dialog::new())]
963+
struct Blueprint {
964+
title: String,
965+
title_position: Option<HAlign>,
993966

994-
let buttons: Vec<Btn> = context.resolve(&config["buttons"])?;
995-
for btn in buttons {
996-
dialog.add_button_with_cb(btn.key, btn.value);
997-
}
967+
content: Option<BoxedView>,
998968

999-
if let Some(focus) = context.resolve(&config["focus"])? {
1000-
dialog.set_focus(focus);
1001-
}
969+
#[blueprint(foreach=add_button_with_cb(buttons.key, buttons.value))]
970+
buttons: Vec<Btn>,
1002971

1003-
Ok(dialog)
1004-
});
972+
padding: Option<Margins>,
973+
focus: Option<DialogFocus>,
974+
}
1005975

1006976
/*
1007977
#[crate::fn_blueprint(Dialog::info())]

cursive-macros/src/builder/blueprint.rs

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ fn parse_enum(
148148
// )]
149149
struct VariableSpecs {
150150
no_config: bool,
151-
setter: Option<syn::Ident>,
152-
foreach: Option<syn::Ident>,
151+
setter: Option<syn::Expr>,
152+
foreach: Option<syn::Expr>,
153153
callback: Option<bool>,
154154
config_name: Option<String>,
155155
default: Option<syn::Expr>,
@@ -319,33 +319,7 @@ impl Variable {
319319
Consumer::Noop
320320
} else {
321321
let inferred_type = looks_inferred(&field.ty);
322-
match (specs.setter, specs.foreach, specs.callback, inferred_type) {
323-
(None, None, None | Some(false), false) | (None, None, Some(false), true) => {
324-
// Default case: use a setter based on the ident.
325-
let setter = format!("set_{parameter_name}");
326-
Consumer::Setter(Setter {
327-
method: syn::Ident::new(&setter, Span::call_site()),
328-
})
329-
}
330-
(Some(setter), None, None | Some(false), _) => {
331-
// Explicit setter function
332-
Consumer::Setter(Setter { method: setter })
333-
}
334-
(None, Some(foreach), None | Some(false), _) => {
335-
// Foreach function (like `add_item`)
336-
Consumer::ForEach(Box::new(Consumer::Setter(Setter { method: foreach })))
337-
}
338-
(None, None, Some(true), _) | (None, None, _, true) => {
339-
// TODO: Check that the type is iterable? A Vec?
340-
341-
// Callback flag means we use the callback_helper-generated `_cb` setter.
342-
let setter = format!("set_{parameter_name}_cb");
343-
Consumer::Setter(Setter {
344-
method: syn::Ident::new(&setter, Span::call_site()),
345-
})
346-
}
347-
_ => panic!("unsupported configuration"),
348-
}
322+
Consumer::parse(&specs, inferred_type, &parameter_name)
349323
};
350324

351325
// Some types have special handling
@@ -372,8 +346,11 @@ impl Variable {
372346
}
373347
}
374348

375-
struct Setter {
376-
method: syn::Ident,
349+
enum Setter {
350+
/// A method call, implicitly applied to the target object.
351+
Call(syn::ExprCall),
352+
/// An ident, the name of the method to call, with a single arg.
353+
Ident(syn::Ident),
377354
}
378355

379356
impl Setter {
@@ -382,9 +359,30 @@ impl Setter {
382359
base_ident: &syn::Ident,
383360
field_ident: &syn::Ident,
384361
) -> proc_macro2::TokenStream {
385-
let function = &self.method;
386-
quote! {
387-
#base_ident.#function(#field_ident);
362+
match self {
363+
Self::Call(call) => {
364+
// Here we don't care about the field ident.
365+
quote! {
366+
#base_ident.#call;
367+
}
368+
}
369+
Self::Ident(function) => {
370+
quote! {
371+
#base_ident.#function(#field_ident);
372+
}
373+
}
374+
}
375+
}
376+
377+
fn parse(expr: &syn::Expr) -> Self {
378+
match expr {
379+
syn::Expr::Path(path) => {
380+
let ident = path.path.get_ident().unwrap();
381+
Self::Ident(ident.clone())
382+
}
383+
syn::Expr::Call(call) => Self::Call(call.clone()),
384+
385+
_ => panic!("Unsupported setter expression ({expr:?})"),
388386
}
389387
}
390388
}
@@ -401,6 +399,7 @@ enum Consumer {
401399
// The value is a Vec<T> and we need to call something on each item.
402400
ForEach(Box<Consumer>),
403401

402+
// Optional consumer - unwraps an option and calls the inner consumer.
404403
Opt(Box<Consumer>),
405404

406405
// We need to call this method to "set" the value.
@@ -436,6 +435,37 @@ impl Consumer {
436435
}
437436
}
438437
}
438+
439+
fn parse(specs: &VariableSpecs, inferred_type: bool, parameter_name: &str) -> Self {
440+
match (
441+
&specs.setter,
442+
&specs.foreach,
443+
&specs.callback,
444+
inferred_type,
445+
) {
446+
(None, None, None | Some(false), false) | (None, None, Some(false), true) => {
447+
// Default case: use a setter based on the ident.
448+
let setter = format!("set_{parameter_name}");
449+
Consumer::Setter(Setter::Ident(syn::Ident::new(&setter, Span::call_site())))
450+
}
451+
(Some(setter), None, None | Some(false), _) => {
452+
// Explicit setter function
453+
Consumer::Setter(Setter::parse(setter))
454+
}
455+
(None, Some(foreach), None | Some(false), _) => {
456+
// Foreach function (like `add_item`)
457+
Consumer::ForEach(Box::new(Consumer::Setter(Setter::parse(foreach))))
458+
}
459+
(None, None, Some(true), _) | (None, None, _, true) => {
460+
// TODO: Check that the type is iterable? A Vec?
461+
462+
// Callback flag means we use the callback_helper-generated `_cb` setter.
463+
let setter = format!("set_{parameter_name}_cb");
464+
Consumer::Setter(Setter::Ident(syn::Ident::new(&setter, Span::call_site())))
465+
}
466+
_ => panic!("unsupported configuration"),
467+
}
468+
}
439469
}
440470

441471
// Returns the quote!d code to build the object using this struct.

0 commit comments

Comments
 (0)