mod parse_enum;
mod templates;
use crate::parse_enum::*;
use crate::templates::*;
extern crate proc_macro;
#[allow(unused_imports)]
use std::any::type_name;
#[allow(unused_imports)]
use std::iter::Enumerate;
use proc_macro::TokenStream;
use quote::ToTokens;
use std::collections::HashMap;
use syn::{self, Data, DeriveInput, GenericParam, Ident};
use tera::*;
#[proc_macro_derive(VariantAccess)]
pub fn variant_access_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
impl_variant_access(&ast)
}
fn impl_contains_variant(
ast: &DeriveInput,
name: &str,
params: &[String],
types: &HashMap<String, &Ident>,
templater: &Tera,
) -> TokenStream {
let (param_string, generic) = if !params.is_empty() {
(
format!("<{}>", ast.generics.params.to_token_stream()),
params.concat(),
)
} else {
(String::from(""), String::from("T"))
};
let mut context = Context::new();
context.insert("generics", ¶m_string);
context.insert("template", &generic);
context.insert("fullname", &name);
context.insert(
"matches",
&types
.keys()
.map(|type_| format!("std::any::TypeId::of::<{}>()", type_))
.collect::<Vec<String>>(),
);
context.insert(
"branches",
&types
.iter()
.map(|(type_, field_)| {
format!(
"{}::{}(_) => Ok(std::any::TypeId::of::<{}>()",
&ast.ident.to_string(),
field_.to_string(),
type_
)
})
.collect::<Vec<String>>(),
);
let impl_string = templater
.render("contains_variant", &context)
.expect("Failed to fill in ContainsVariant template");
impl_string.parse().unwrap()
}
fn impl_get_variant(
ast: &DeriveInput,
name: &str,
params: &[String],
types: &HashMap<String, &Ident>,
templater: &Tera,
) -> TokenStream {
let mut impl_string = String::new();
let generics = if !params.is_empty() {
format!("<{}>", ast.generics.params.to_token_stream())
} else {
String::from("")
};
for (type_, field_) in types.iter() {
let mut context = Context::new();
context.insert("generics", &generics);
context.insert("Type", &type_);
context.insert(
"Marker",
&format!(
"variant_access_{}::{}",
ast.ident.to_string(),
field_.to_string()
),
);
context.insert("fullname", name);
context.insert("name", &ast.ident.to_string());
context.insert("field", &field_.to_string());
impl_string.push_str(
&templater
.render("get_variant", &context)
.expect("Failed to fill in GetVariant template"),
);
}
impl_string.parse().unwrap()
}
fn impl_set_variant(
ast: &DeriveInput,
name: &str,
params: &[String],
types: &HashMap<String, &Ident>,
templater: &Tera,
) -> TokenStream {
let mut impl_string = String::new();
let generics = if !params.is_empty() {
format!("<{}>", ast.generics.params.to_token_stream())
} else {
String::from("")
};
for (type_, field_) in types.iter() {
let mut context = Context::new();
context.insert("generics", &generics);
context.insert("Type", &type_);
context.insert(
"Marker",
&format!(
"variant_access_{}::{}",
ast.ident.to_string(),
field_.to_string()
),
);
context.insert("fullname", name);
context.insert("name", &ast.ident.to_string());
context.insert("field", &field_.to_string());
impl_string.push_str(
&templater
.render("set_variant", &context)
.expect("Failed to fill in GetVariant template"),
);
}
impl_string.parse().unwrap()
}
fn impl_create_variant(
ast: &DeriveInput,
name: &str,
params: &[String],
types: &HashMap<String, &Ident>,
templater: &Tera,
) -> TokenStream {
let mut impl_string = String::new();
let generics = if !params.is_empty() {
format!("<{}>", &ast.generics.params.to_token_stream())
} else {
String::from("")
};
for (type_, field_) in types.iter() {
let mut context = Context::new();
context.insert("generics", &generics);
context.insert("Type", &type_);
context.insert(
"Marker",
&format!(
"variant_access_{}::{}",
ast.ident.to_string(),
field_.to_string()
),
);
context.insert("fullname", name);
context.insert("name", &ast.ident.to_string());
context.insert("field", &field_.to_string());
impl_string.push_str(
&templater
.render("create_variant", &context)
.expect("Failed to fill in CreateVariantFrom template"),
);
}
impl_string.parse().unwrap()
}
fn impl_variant_access(ast: &DeriveInput) -> TokenStream {
let mut tera = Tera::new("/dev/null/*").unwrap();
tera.add_raw_template("contains_variant", CONTAINS_VARIANT_TEMPLATE)
.unwrap();
tera.add_raw_template("get_variant", GET_VARIANT_TEMPLATE)
.unwrap();
tera.add_raw_template("set_variant", SET_VARIANT_TEMPLATE)
.unwrap();
tera.add_raw_template("create_variant", CREATE_VARIANT_TEMPLATE)
.unwrap();
let mut tokens: TokenStream = "".parse().unwrap();
let (name, params) = fetch_name_with_generic_params(&ast);
let types = fetch_types_from_enum(&ast);
tokens.extend::<TokenStream>(create_marker_structs(&ast.ident.to_string(), &types));
tokens.extend::<TokenStream>(impl_contains_variant(&ast, &name, ¶ms, &types, &tera));
tokens.extend::<TokenStream>(impl_get_variant(&ast, &name, ¶ms, &types, &tera));
tokens.extend::<TokenStream>(impl_set_variant(&ast, &name, ¶ms, &types, &tera));
tokens.extend::<TokenStream>(impl_create_variant(&ast, &name, ¶ms, &types, &tera));
tokens
}