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)) }