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}