cursive_macros/
lib.rs

1use proc_macro::TokenStream;
2
3mod builder;
4
5/// Generate two helper functions to help working with cursive blueprints.
6///
7/// # Problem to solve
8///
9/// When writing cursive blueprints, it is often necessary to load parameters or variables.
10///
11/// Some of these have simple types like `u64` or `String`, but in some cases we need to load a
12/// callback.
13///
14/// In this case, the blueprint loading the variable and the user storing the variable need
15/// to use the exact same type, or downcasting will not work. This is made complicated by Rust's
16/// closures, where each closure is a unique anonymous type: if the user directly stores a closure,
17/// there will be no way to identify its exact type to downcast it in the blueprint.
18///
19/// Instead, both sides (blueprint and user) need to agree to use a fixed type, for example a trait
20/// object like `Arc<dyn Fn(&mut Cursive)>` (we use `Arc` rather than `Box` because we want variables
21/// to be cloneable).
22///
23/// It's a bit cumbersome having to write the exact type including the `Arc` whenever we want to
24/// store a callback for a blueprint. Similarly, it's a bit annoying when writing the blueprint to make
25/// sure the correct `Arc<...>` type is fetched and converted to a type directly usable as callback.
26///
27/// # Solution
28///
29/// This is where this macro comes into play: from an original function that uses a callback, it
30/// generates two helper functions:
31///
32/// * A _maker_ function, to be used when storing variables. This function takes a generic type
33///   implementing the same `Fn` trait as the desired callback, and returns it wrapped in the correct
34///   trait object.
35/// * A _setter_ function, to be used when writing blueprints. This function wraps the original
36///   function, but takes a trait-object instead of a generic `Fn` type, and unwraps it internally.
37///
38/// # Notes
39///
40/// * The wrapped function doesn't even have to take `self`, it can be a "static"
41///   constructor method.
42/// * The `maker` function always takes a `Fn`, not a `FnMut` or `FnOnce`.
43///   Use the `cursive::immut1!` (and others) macros to wrap a `FnMut` if you need it.
44///
45/// # Examples
46///
47/// ```rust,ignore
48/// struct Foo {
49///     callback: Box<dyn Fn(&mut Cursive)>,
50/// }
51///
52/// impl Foo {
53///     #[cursive::callback_helpers]
54///     pub fn new<F>(callback: F) -> Self
55///     where
56///         F: Fn(&mut Cursive) + 'static,
57///     {
58///         let callback = Box::new(callback);
59///         Foo { callback }
60///     }
61/// }
62///
63/// cursive::blueprint!(Foo, |config, context| {
64///     let foo =
65///         Foo::new_with_cb(context.resolve(config["callback"])?);
66///
67///     Ok(foo)
68/// });
69///
70/// // Elsewhere
71/// fn foo() {
72///     let mut context = cursive::builder::Context::new();
73///
74///     context.store("callback", Foo::new_cb(|s| s.quit()));
75/// }
76/// ```
77#[proc_macro_attribute]
78pub fn callback_helpers(_attrs: TokenStream, item: TokenStream) -> TokenStream {
79    builder::callback_helpers(item)
80}
81
82/// Defines a blueprint for creating a view from config.
83///
84/// It should be added to a type which defines how to build the view.
85///
86/// # Examples
87///
88/// ```rust,ignore
89/// #[cursive::blueprint(TextView::empty())]
90/// struct BlueprintForTextview {
91///   content: StyledString,
92/// }
93/// ```
94///
95/// This recipe will:
96/// * Create a base view with `TextView::empty()`.
97/// * Look for a `content` key in the given config.
98/// * Try to resolve the associated value to a `StyledString`.
99/// * Call `set_content` on the base with the resulting `StyledString`.
100#[proc_macro_attribute]
101pub fn blueprint(attrs: TokenStream, item: TokenStream) -> TokenStream {
102    builder::blueprint(attrs, item)
103}