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