bevy_tnua_macros/
lib.rs

1use proc_macro2::TokenStream;
2use syn::{DeriveInput, parse::Error, parse_macro_input, spanned::Spanned};
3
4use self::scheme_derive::codegen::generate_scheme_derive;
5use self::scheme_derive::parsed::ParsedScheme;
6
7mod scheme_derive;
8#[allow(unused)]
9mod util;
10
11/// Make an enum a control scheme for a Tnua character controller.
12///
13/// This implements the `TnuaScheme` trait for the enum, and also generates the following structs
14/// required for implementing it (replace `{name}` with the name of the control scheme enum):
15///
16/// * `{name}Config` - a struct with the configuration of the basis and all the actions.
17/// * `{name}ActionDiscriminant` - an enum mirroring the control scheme, except all the variants
18///   are units.
19/// * `{name}ActionState` - an enum mirroring the control scheme, except instead of just the input
20///   types each variant contains a `TnuaActionState` which holds the input, configuration and
21///   memory of the action.
22///
23/// The enum itself **must** have a `#[scheme(basis = ...)]` attribute that specifies the basis of
24/// the control scheme (typically `TnuaBuiltinWalk`). The following additional parameters are
25/// allowed on that `scheme` attribute on the enum:
26///
27/// * `#[scheme(serde)]` - derive Serialize and Deserialize on the generated action state enum.
28///   * This is mostly useful with (and will probably fail without) the `serialize` feature enabled
29///     on the bevy-tnua crate.
30///   * The control scheme enum itself will not get these derives automatically - that derive will
31///     need to be added manually.
32///   * With these, and with the `serialize` feature enabled, the `TnuaController` and
33///     `TnuaGhostOverwrites` of the control scheme will also be serializable and deserializable -
34///     allowing networking libraries to synchronize them between machines.
35///   * Even without this setting and without the `serialize` feature on the bevy-tnua crate, the
36///     generated configuration struct and the action discriminant enum will still get these
37///     derives.
38///
39/// Each variant **must** be a tuple variant, where the first element of the tuple is the action,
40/// followed by zero or more payloads.
41///
42/// Payloads are ignored by Tnua itself - they are for the user systems to keep track of data
43/// related to the actions - except when they are annotated by `#[scheme(modify_basis_config)]`.
44/// Such payloads will modify the configuration when the action they are part of is in effect.
45///
46/// Example:
47///
48/// ```ignore
49/// #[derive(TnuaScheme)]
50/// #[scheme(basis = TnuaBuiltinWalk)]
51/// pub enum DemoControlScheme {
52///     Jump(TnuaBuiltinJump),
53///     Crouch(
54///         TnuaBuiltinCrouch,
55///         // While this action is in effect, `SlowDownWhileCrouching` will change the
56///         // `TnuaBuiltinWalkConfig` to reduce character speed.
57///         #[scheme(modify_basis_config)] SlowDownWhileCrouching,
58///     ),
59///     WallSlide(
60///         TnuaBuiltinWallSlide,
61///         // This payload has is ignored by Tnua, but user code can use it to tell which wall
62///         // the character is sliding on.
63///         Entity,
64///     ),
65/// }
66/// ```
67///
68#[proc_macro_derive(TnuaScheme, attributes(scheme))]
69pub fn derive_tnua_scheme(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
70    let input = parse_macro_input!(input as DeriveInput);
71    match impl_derive_tnua_scheme(&input) {
72        Ok(output) => output.into(),
73        Err(error) => error.to_compile_error().into(),
74    }
75}
76
77fn impl_derive_tnua_scheme(ast: &syn::DeriveInput) -> Result<TokenStream, Error> {
78    Ok(match &ast.data {
79        syn::Data::Struct(_) => {
80            return Err(Error::new(
81                ast.span(),
82                "TnuaScheme is not supported for structs - only for enums",
83            ));
84        }
85        syn::Data::Enum(data_enum) => {
86            let parsed = ParsedScheme::new(ast, data_enum)?;
87            generate_scheme_derive(&parsed)?
88        }
89        syn::Data::Union(_) => {
90            return Err(Error::new(
91                ast.span(),
92                "TnuaScheme is not supported for unions - only for enums",
93            ));
94        }
95    })
96}