specs_derive/
lib.rs

1//! Implements the `#[derive(Component)]`, `#[derive(Saveload)]` macro and
2//! `#[component]` attribute for [Specs][sp].
3//!
4//! [sp]: https://slide-rs.github.io/specs-website/
5
6#![recursion_limit = "128"]
7
8extern crate proc_macro;
9extern crate proc_macro2;
10#[macro_use]
11extern crate quote;
12#[macro_use]
13extern crate syn;
14
15use proc_macro::TokenStream;
16use syn::{
17    parse::{Parse, ParseStream, Result},
18    DeriveInput, Path,
19};
20
21mod impl_saveload;
22
23/// Custom derive macro for the `Component` trait.
24///
25/// ## Example
26///
27/// ```rust,ignore
28/// use specs::storage::VecStorage;
29///
30/// #[derive(Component, Debug)]
31/// #[storage(VecStorage)] // This line is optional, defaults to `DenseVecStorage`
32/// struct Pos(f32, f32, f32);
33/// ```
34#[proc_macro_derive(Component, attributes(storage))]
35pub fn component(input: TokenStream) -> TokenStream {
36    let ast = syn::parse(input).unwrap();
37    let gen = impl_component(&ast);
38    gen.into()
39}
40
41struct StorageAttribute {
42    storage: Path,
43}
44
45impl Parse for StorageAttribute {
46    fn parse(input: ParseStream) -> Result<Self> {
47        let content;
48        let _parenthesized_token = parenthesized!(content in input);
49
50        Ok(StorageAttribute {
51            storage: content.parse()?,
52        })
53    }
54}
55
56fn impl_component(ast: &DeriveInput) -> proc_macro2::TokenStream {
57    let name = &ast.ident;
58    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
59
60    let storage = ast
61        .attrs
62        .iter()
63        .find(|attr| attr.path.segments[0].ident == "storage")
64        .map(|attr| {
65            syn::parse2::<StorageAttribute>(attr.tokens.clone())
66                .unwrap()
67                .storage
68        })
69        .unwrap_or_else(|| parse_quote!(DenseVecStorage));
70
71    quote! {
72        impl #impl_generics Component for #name #ty_generics #where_clause {
73            type Storage = #storage<Self>;
74        }
75    }
76}
77
78/// Custom derive macro for the `ConvertSaveload` trait.
79///
80/// Requires `Entity`, `ConvertSaveload`, `Marker` and `NoError` to be in a
81/// scope.
82///
83/// ## Example
84///
85/// ```rust,ignore
86/// use specs::{Entity, saveload::{ConvertSaveload, Marker}, error::NoError};
87///
88/// #[derive(ConvertSaveload)]
89/// struct Target(Entity);
90/// ```
91#[proc_macro_derive(ConvertSaveload)]
92pub fn saveload(input: TokenStream) -> TokenStream {
93    use impl_saveload::impl_saveload;
94    let mut ast = syn::parse(input).unwrap();
95
96    let gen = impl_saveload(&mut ast);
97    gen.into()
98}