variant_map_derive/
lib.rs

1//! Enum variants stored in Maps.
2//!
3//! Provides derive macros for `variant_map`
4//!
5//! Includes a `StructMap` which is a struct with a field per variant of the enum
6//!
7//! Pro: This struct has instant access to the fields (compared to the other Maps that need a lookup)
8//!
9//! Con: Restricted API
10//!
11//! # Example
12//!
13//! ```
14//!     use variant_map_derive::VariantStore;
15//!
16//!     #[derive(VariantStore)]
17//!     enum MyEnum {
18//!         A,
19//!         B(i32),
20//!     }
21//! ```
22//!
23//! For more detailed examples check out the [example project](https://github.com/mxyns/variant-map/tree/master/example) on this crates' [repo](https://github.com/mxyns/variant-map/)
24//!
25
26/// Parameters of the macros attributes
27pub(crate) mod attrs;
28
29/// Helper functions for the [maps] and [structs] implementations
30pub(crate) mod common;
31
32/// Implementation of the derive for variant_map
33pub(crate) mod maps;
34
35/// Implementation of the derive for `StructMap`
36/// This type is derive-only (not included in the base crate)
37///
38/// This macro expansion contains a new Key Enum, a `struct` specific to the enum type
39/// with one field per variant
40///
41/// It also features implementation of the same traits as a normal variant Map
42pub(crate) mod structs;
43
44use crate::attrs::{MapType, BaseAttr};
45use crate::common::EnumType;
46use darling::FromDeriveInput;
47use proc_macro::TokenStream;
48use quote::{format_ident, quote};
49use syn::{parse_macro_input, DeriveInput};
50use syn::spanned::Spanned;
51
52// TODO fix "private documentation" rustdoc
53// TODO publish
54// TODO allow using user generated (possibly generic or tuple variant) keys
55// TODO [1/2] add struct and array versions of the "map"
56// TODO? trait for all maps to reduce duplicate code
57// TODO? tight couple Map trait and MapValue if possible
58
59/// The only derive macro of this crate
60///
61/// Apply it on an enum to automatically generate an enum of keys and a map to store the variants
62///
63/// # Arguments
64///
65/// See [attrs::BaseAttr]
66///
67/// See other attributes in [attrs]
68///
69#[proc_macro_derive(VariantStore, attributes(VariantStore, VariantMap, VariantStruct, key_name))]
70pub fn derive(input: TokenStream) -> TokenStream {
71    let ast = parse_macro_input!(input as DeriveInput);
72
73    let enum_name = ast.ident.clone();
74
75    // VariantStore attribute parameters
76    let (key_enum_name, map_type) = {
77        let base_attr = BaseAttr::from_derive_input(&ast).expect("Wrong VariantStore parameters");
78
79        (
80            base_attr.keys_name(format_ident!("{}Key", &enum_name)),
81            base_attr.map_type()
82        )
83    };
84
85    let enum_type = &EnumType {
86        enum_name: &enum_name,
87        generics: &ast.generics,
88    };
89
90    let result = match map_type {
91        MapType::HashMap | MapType::BTreeMap => {
92            maps::generate_map_code(&ast, &map_type, enum_type, &key_enum_name)
93        }
94        MapType::Struct => {
95            structs::generate_struct_code(&ast, &map_type, enum_type, &key_enum_name)
96        }
97    };
98
99    let (out_of_const, inside_const) = match result {
100        Ok( tup ) => tup,
101        Err(_) => (Some(syn::Error::new(ast.span(), "VariantStore works only on enums").into_compile_error()), None)
102    };
103
104    let result = quote! {
105
106        #out_of_const
107
108        #[doc(hidden)]
109        #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
110        const _: () = {
111            #[allow(unused_extern_crates, clippy::useless_attribute)]
112            extern crate variant_map as _variant_map;
113            use _variant_map::common::*;
114            use _variant_map::serde;
115
116            #inside_const
117        };
118    };
119
120    result.into()
121}