genesis_impl/
lib.rs

1#![deny(rust_2018_idioms)]
2#![deny(clippy::all)]
3
4mod component;
5mod input;
6mod template;
7mod world;
8
9use input::*;
10use proc_macro::TokenStream;
11use quote::quote;
12use syn::{parse_macro_input, DeriveInput, Result};
13
14/// Generates an ECS (World) and a component enum containing all components.
15///
16/// Takes as input a struct with named fields.
17/// The names of the fields will correspond to the names of the storage types in the generated World.
18/// The storage type used can be specified with `#[component(vec)]` for `VecStorage<T>` (the default)
19/// or `#[component(map)]` for `MapStorage<T>`.
20///
21/// The name of the generated ECS is passed to the `#[world]` macro directly, together with the name of
22/// the component enum. The component enum is a generated enum with one variant per component type that
23/// can be used to register any of the component types on the generated World as an alternative to
24/// directly calling `.set()` on the corresponding storage field.
25///
26/// The generated ECS has a shared set of `Entities` that is also used by each storage to check if
27/// an entity exists; it is available via the `.entities` field. To avoid concurrency hazards,
28/// it is stored in an `Arc<RwLock<Entities>>`. The generated `World` has some utility methods for
29/// spawning new entities; these are handy shortcuts to accessing the underlying `entities` directly.
30/// When spawning entities in a batch, direct access is recommended to avoid re-acquiring the write
31/// lock over and over.
32///
33/// In addition to the component enum, this macro generates a "template" for an entity;
34/// this template has one public field of type `Option<T>` for every component and can be used
35/// to set the corresponding components on an entity. The name of these fields defaults to the name of the
36/// field in the World definition and can be customized via `#[template_name(name)]`.
37///
38/// Attribute macros like `#[derive(Debug)]` are applied to both the component enum and the
39/// template struct. This can be very useful for debugging and provides a quick and simple way
40/// to define entities in data files and using e.g. serde to deserialize them into the generated
41/// Template struct.
42///
43/// # Example
44/// ```ignore
45/// #[derive(Clone, Debug, Eq, PartialEq)]
46/// pub struct Position {
47///    pub position: (u32, u32),
48/// }
49///
50/// #[derive(Clone, Debug, Eq, PartialEq)]
51/// pub struct NameComponent {
52///     pub name: String,
53/// }
54///
55/// #[derive(Clone, Debug, Eq, PartialEq)]
56/// pub struct RareComponent {
57///     pub data: u32,
58/// }
59///
60/// #[world(MyComponent, Template)]
61/// #[derive(Clone, Debug, Eq, PartialEq)]
62/// pub struct World {
63///     #[component(vec)] //default, optional
64///     positions: Position,
65///     names: NameComponent,
66///     #[component(map)]
67///     rare_data: RareComponent,
68/// }
69/// ```
70#[proc_macro_attribute]
71pub fn world(args: TokenStream, input: TokenStream) -> TokenStream {
72    let input = parse_macro_input!(input as DeriveInput);
73    let args = parse_macro_input!(args as InputArgs);
74    generate_code(args, input).unwrap_or_else(|e| e.to_compile_error().into())
75}
76
77fn generate_code(args: InputArgs, input: DeriveInput) -> Result<TokenStream> {
78    let input = Input::new(args, &input)?;
79    let template_code = template::generate_code(&input);
80    let component_code = component::generate_code(&input);
81    let world_code = world::generate_code(&input);
82
83    let output = quote! {
84        #template_code
85        #component_code
86        #world_code
87    };
88
89    Ok(TokenStream::from(output))
90}