gpui_macros.rs

  1mod derive_action;
  2mod derive_app_context;
  3mod derive_into_element;
  4mod derive_into_view_element;
  5mod derive_render;
  6mod derive_visual_context;
  7mod property_test;
  8mod register_action;
  9mod styles;
 10mod test;
 11
 12#[cfg(any(feature = "inspector", debug_assertions))]
 13mod derive_inspector_reflection;
 14
 15use proc_macro::TokenStream;
 16use syn::{DeriveInput, Ident};
 17
 18/// `Action` derive macro - see the trait documentation for details.
 19#[proc_macro_derive(Action, attributes(action))]
 20pub fn derive_action(input: TokenStream) -> TokenStream {
 21    derive_action::derive_action(input)
 22}
 23
 24/// This can be used to register an action with the GPUI runtime when you want to manually implement
 25/// the `Action` trait. Typically you should use the `Action` derive macro or `actions!` macro
 26/// instead.
 27#[proc_macro]
 28pub fn register_action(ident: TokenStream) -> TokenStream {
 29    register_action::register_action(ident)
 30}
 31
 32/// #[derive(IntoElement)] is used to create a Component out of anything that implements
 33/// the `RenderOnce` trait.
 34#[proc_macro_derive(IntoElement)]
 35pub fn derive_into_element(input: TokenStream) -> TokenStream {
 36    derive_into_element::derive_into_element(input)
 37}
 38
 39/// #[derive(IntoViewElement)] generates an `IntoElement` implementation for types
 40/// that implement the `View` trait. It creates a `ViewElement` wrapper and
 41/// automatically enables caching based on the `View::style()` method.
 42#[proc_macro_derive(IntoViewElement)]
 43pub fn derive_into_view_element(input: TokenStream) -> TokenStream {
 44    derive_into_view_element::derive_into_view_element(input)
 45}
 46
 47#[proc_macro_derive(Render)]
 48#[doc(hidden)]
 49pub fn derive_render(input: TokenStream) -> TokenStream {
 50    derive_render::derive_render(input)
 51}
 52
 53/// #[derive(AppContext)] is used to create a context out of anything that holds a `&mut App`
 54/// Note that a `#[app]` attribute is required to identify the variable holding the &mut App.
 55///
 56/// Failure to add the attribute causes a compile error:
 57///
 58/// ```compile_fail
 59/// # #[macro_use] extern crate gpui_macros;
 60/// # #[macro_use] extern crate gpui;
 61/// #[derive(AppContext)]
 62/// struct MyContext<'a> {
 63///     app: &'a mut gpui::App
 64/// }
 65/// ```
 66#[proc_macro_derive(AppContext, attributes(app))]
 67pub fn derive_app_context(input: TokenStream) -> TokenStream {
 68    derive_app_context::derive_app_context(input)
 69}
 70
 71/// #[derive(VisualContext)] is used to create a visual context out of anything that holds a `&mut Window` and
 72/// implements `AppContext`
 73/// Note that a `#[app]` and a `#[window]` attribute are required to identify the variables holding the &mut App,
 74/// and &mut Window respectively.
 75///
 76/// Failure to add both attributes causes a compile error:
 77///
 78/// ```compile_fail
 79/// # #[macro_use] extern crate gpui_macros;
 80/// # #[macro_use] extern crate gpui;
 81/// #[derive(VisualContext)]
 82/// struct MyContext<'a, 'b> {
 83///     #[app]
 84///     app: &'a mut gpui::App,
 85///     window: &'b mut gpui::Window
 86/// }
 87/// ```
 88///
 89/// ```compile_fail
 90/// # #[macro_use] extern crate gpui_macros;
 91/// # #[macro_use] extern crate gpui;
 92/// #[derive(VisualContext)]
 93/// struct MyContext<'a, 'b> {
 94///     app: &'a mut gpui::App,
 95///     #[window]
 96///     window: &'b mut gpui::Window
 97/// }
 98/// ```
 99#[proc_macro_derive(VisualContext, attributes(window, app))]
100pub fn derive_visual_context(input: TokenStream) -> TokenStream {
101    derive_visual_context::derive_visual_context(input)
102}
103
104/// Used by GPUI to generate the style helpers.
105#[proc_macro]
106#[doc(hidden)]
107pub fn style_helpers(input: TokenStream) -> TokenStream {
108    styles::style_helpers(input)
109}
110
111/// Generates methods for visibility styles.
112#[proc_macro]
113pub fn visibility_style_methods(input: TokenStream) -> TokenStream {
114    styles::visibility_style_methods(input)
115}
116
117/// Generates methods for margin styles.
118#[proc_macro]
119pub fn margin_style_methods(input: TokenStream) -> TokenStream {
120    styles::margin_style_methods(input)
121}
122
123/// Generates methods for padding styles.
124#[proc_macro]
125pub fn padding_style_methods(input: TokenStream) -> TokenStream {
126    styles::padding_style_methods(input)
127}
128
129/// Generates methods for position styles.
130#[proc_macro]
131pub fn position_style_methods(input: TokenStream) -> TokenStream {
132    styles::position_style_methods(input)
133}
134
135/// Generates methods for overflow styles.
136#[proc_macro]
137pub fn overflow_style_methods(input: TokenStream) -> TokenStream {
138    styles::overflow_style_methods(input)
139}
140
141/// Generates methods for cursor styles.
142#[proc_macro]
143pub fn cursor_style_methods(input: TokenStream) -> TokenStream {
144    styles::cursor_style_methods(input)
145}
146
147/// Generates methods for border styles.
148#[proc_macro]
149pub fn border_style_methods(input: TokenStream) -> TokenStream {
150    styles::border_style_methods(input)
151}
152
153/// Generates methods for box shadow styles.
154#[proc_macro]
155pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
156    styles::box_shadow_style_methods(input)
157}
158
159/// `#[gpui::test]` can be used to annotate test functions that run with GPUI support.
160///
161/// It supports both synchronous and asynchronous tests, and can provide you with
162/// as many `TestAppContext` instances as you need.
163/// The output contains a `#[test]` annotation so this can be used with any existing
164/// test harness (`cargo test` or `cargo-nextest`).
165///
166/// ```
167/// #[gpui::test]
168/// async fn test_foo(mut cx: &TestAppContext) { }
169/// ```
170///
171/// In addition to passing a TestAppContext, you can also ask for a `StdRnd` instance.
172/// this will be seeded with the `SEED` environment variable and is used internally by
173/// the ForegroundExecutor and BackgroundExecutor to run tasks deterministically in tests.
174/// Using the same `StdRng` for behavior in your test will allow you to exercise a wide
175/// variety of scenarios and interleavings just by changing the seed.
176///
177/// # Arguments
178///
179/// - `#[gpui::test]` with no arguments runs once with the seed `0` or `SEED` env var if set.
180/// - `#[gpui::test(seed = 10)]` runs once with the seed `10`.
181/// - `#[gpui::test(seeds(10, 20, 30))]` runs three times with seeds `10`, `20`, and `30`.
182/// - `#[gpui::test(iterations = 5)]` runs five times, providing as seed the values in the range `0..5`.
183/// - `#[gpui::test(retries = 3)]` runs up to four times if it fails to try and make it pass.
184/// - `#[gpui::test(on_failure = "crate::test::report_failure")]` will call the specified function after the
185///   tests fail so that you can write out more detail about the failure.
186///
187/// You can combine `iterations = ...` with `seeds(...)`:
188/// - `#[gpui::test(iterations = 5, seed = 10)]` is equivalent to `#[gpui::test(seeds(0, 1, 2, 3, 4, 10))]`.
189/// - `#[gpui::test(iterations = 5, seeds(10, 20, 30)]` is equivalent to `#[gpui::test(seeds(0, 1, 2, 3, 4, 10, 20, 30))]`.
190/// - `#[gpui::test(seeds(10, 20, 30), iterations = 5]` is equivalent to `#[gpui::test(seeds(0, 1, 2, 3, 4, 10, 20, 30))]`.
191///
192/// # Environment Variables
193///
194/// - `SEED`: sets a seed for the first run
195/// - `ITERATIONS`: forces the value of the `iterations` argument
196#[proc_macro_attribute]
197pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
198    test::test(args, function)
199}
200
201/// A variant of `#[gpui::test]` that supports property-based testing.
202///
203/// A property test, much like a standard GPUI randomized test, allows testing
204/// claims of the form "for any possible X, Y should hold". For example:
205/// ```
206/// #[gpui::property_test]
207/// fn test_arithmetic(x: i32, y: i32) {
208///     assert!(x == y || x < y || x > y);
209/// }
210/// ```
211/// Standard GPUI randomized tests provide you with an instance of `StdRng` to
212/// generate random data in a controlled manner. Property-based tests have some
213/// advantages, however:
214/// - Shrinking - the harness also understands a notion of the "complexity" of a
215///   particular value. This allows it to find the "simplest possible value that
216///   causes the test to fail".
217/// - Ergonomics/clarity - the property-testing harness will automatically
218///   generate values, removing the need to fill the test body with generation
219///   logic.
220/// - Failure persistence - if a failing seed is identified, it is stored in a
221///   file, which can be checked in, and future runs will check these cases before
222///   future cases.
223///
224/// Property tests work best when all inputs can be generated up-front and kept
225/// in a simple data structure. Sometimes, this isn't possible - for example, if
226/// a test needs to make a random decision based on the current state of some
227/// structure. In this case, a standard GPUI randomized test may be more
228/// suitable.
229///
230/// ## Customizing random values
231///
232/// This macro is based on the [`#[proptest::property_test]`] macro, but handles
233/// some of the same GPUI-specific arguments as `#[gpui::test]`. Specifically,
234/// `&{mut,} TestAppContext` and `BackgroundExecutor` work as normal. `StdRng`
235/// arguments are **explicitly forbidden**, since they break shrinking, and are
236/// a common footgun.
237///
238/// All other arguments are forwarded to the underlying proptest macro.
239///
240/// Note: much of the following is copied from the proptest docs, specifically the
241/// [`#[proptest::property_test]`] macro docs.
242///
243/// Random values of type `T` are generated by a `Strategy<Value = T>` object.
244/// Some types have a canonical `Strategy` - these types also implement
245/// `Arbitrary`. Parameters to a `#[gpui::property_test]`, by default, use a
246/// type's `Arbitrary` implementation. If you'd like to provide a custom
247/// strategy, you can use `#[strategy = ...]` on the argument:
248/// ```
249/// #[gpui::property_test]
250/// fn int_test(#[strategy = 1..10] x: i32, #[strategy = "[a-zA-Z0-9]{20}"] s: String) {
251///   assert!(s.len() > (x as usize));
252/// }
253/// ```
254///
255/// For more information on writing custom `Strategy` and `Arbitrary`
256/// implementations, see [the proptest book][book], and the [`Strategy`] trait.
257///
258/// ## Scheduler
259///
260/// Similar to `#[gpui::test]`, this macro will choose random seeds for the test
261/// scheduler. It uses `.no_shrink()` to tell proptest that all seeds are
262/// roughly equivalent in terms of "complexity". If `$SEED` is set, it will
263/// affect **ONLY** the seed passed to the scheduler. To control other values,
264/// use custom `Strategy`s.
265///
266/// [`#[proptest::property_test]`]: https://docs.rs/proptest/latest/proptest/attr.property_test.html
267/// [book]: https://proptest-rs.github.io/proptest/intro.html
268/// [`Strategy`]: https://docs.rs/proptest/latest/proptest/strategy/trait.Strategy.html
269#[proc_macro_attribute]
270pub fn property_test(args: TokenStream, function: TokenStream) -> TokenStream {
271    property_test::test(args.into(), function.into()).into()
272}
273
274/// When added to a trait, `#[derive_inspector_reflection]` generates a module which provides
275/// enumeration and lookup by name of all methods that have the shape `fn method(self) -> Self`.
276/// This is used by the inspector so that it can use the builder methods in `Styled` and
277/// `StyledExt`.
278///
279/// The generated module will have the name `<snake_case_trait_name>_reflection` and contain the
280/// following functions:
281///
282/// ```ignore
283/// pub fn methods::<T: TheTrait + 'static>() -> Vec<gpui::inspector_reflection::FunctionReflection<T>>;
284///
285/// pub fn find_method::<T: TheTrait + 'static>() -> Option<gpui::inspector_reflection::FunctionReflection<T>>;
286/// ```
287///
288/// The `invoke` method on `FunctionReflection` will run the method. `FunctionReflection` also
289/// provides the method's documentation.
290#[cfg(any(feature = "inspector", debug_assertions))]
291#[proc_macro_attribute]
292pub fn derive_inspector_reflection(_args: TokenStream, input: TokenStream) -> TokenStream {
293    derive_inspector_reflection::derive_inspector_reflection(_args, input)
294}
295
296pub(crate) fn get_simple_attribute_field(ast: &DeriveInput, name: &'static str) -> Option<Ident> {
297    match &ast.data {
298        syn::Data::Struct(data_struct) => data_struct
299            .fields
300            .iter()
301            .find(|field| field.attrs.iter().any(|attr| attr.path().is_ident(name)))
302            .map(|field| field.ident.clone().unwrap()),
303        syn::Data::Enum(_) => None,
304        syn::Data::Union(_) => None,
305    }
306}