34: Support for adding derives r=JoelWachsler a=JoelWachsler



Co-authored-by: Joel Wachsler <JoelWachsler@users.noreply.github.com>
This commit is contained in:
bors[bot] 2022-07-24 00:08:01 +00:00 committed by GitHub
commit 73bfa6926c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 167 additions and 45 deletions

View File

@ -5,7 +5,20 @@ use quote::quote;
use super::{group_method::GroupMethod, skeleton::auth_ident, util}; use super::{group_method::GroupMethod, skeleton::auth_ident, util};
impl parser::ApiGroup { #[derive(Debug)]
pub struct GroupGeneration {
api_group: parser::ApiGroup,
response_derives: Vec<String>,
}
impl GroupGeneration {
pub fn new(api_group: parser::ApiGroup, response_derives: Vec<String>) -> Self {
Self {
api_group,
response_derives,
}
}
pub fn generate(&self) -> TokenStream { pub fn generate(&self) -> TokenStream {
let struct_name = self.struct_name(); let struct_name = self.struct_name();
let group_name_snake = self.name_snake(); let group_name_snake = self.name_snake();
@ -45,7 +58,7 @@ impl parser::ApiGroup {
let auth = auth_ident(); let auth = auth_ident();
util::add_docs( util::add_docs(
&self.description, self.description(),
quote! { quote! {
impl super::#auth { impl super::#auth {
pub fn #name_snake(&self) -> #struct_name { pub fn #name_snake(&self) -> #struct_name {
@ -69,21 +82,57 @@ impl parser::ApiGroup {
} }
fn group_methods(&self) -> Vec<GroupMethod> { fn group_methods(&self) -> Vec<GroupMethod> {
self.methods self.methods()
.iter() .iter()
.map(|method| GroupMethod::new(self, method)) .map(|method| GroupMethod::new(self, method))
.collect() .collect()
} }
fn description(&self) -> &Option<String> {
&self.api_group.description
}
fn methods(&self) -> &Vec<parser::ApiMethod> {
&self.api_group.methods
}
pub fn url(&self) -> String {
self.api_group.url.clone()
}
pub fn struct_name(&self) -> Ident { pub fn struct_name(&self) -> Ident {
self.name_camel() self.name_camel()
} }
pub fn response_derives(&self, additional_derives: Vec<&str>) -> TokenStream {
let derives = self
.all_derives()
.chain(additional_derives.into_iter())
.map(|s| syn::parse_str::<syn::Path>(s).unwrap())
.map(|derive| quote! { #derive });
quote! {
#[derive(#(#derives),*)]
}
}
fn all_derives(&self) -> impl Iterator<Item = &str> {
let base = vec!["serde::Deserialize", "Debug"].into_iter();
let additional = self
.response_derives
.iter()
.map(|s| s.as_str())
.filter(|item| item != &"serde::Deserialize")
.filter(|item| item != &"Debug");
base.chain(additional)
}
fn name_camel(&self) -> Ident { fn name_camel(&self) -> Ident {
util::to_ident(&self.name.to_camel()) util::to_ident(&self.api_group.name.to_camel())
} }
fn name_snake(&self) -> Ident { fn name_snake(&self) -> Ident {
util::to_ident(&self.name.to_snake()) util::to_ident(&self.api_group.name.to_snake())
} }
} }

View File

@ -1,30 +1,11 @@
use case::CaseExt; use case::CaseExt;
use proc_macro2::{Ident, TokenStream}; use proc_macro2::Ident;
use quote::quote;
use crate::parser; use crate::parser;
use super::util; use super::util;
impl parser::ApiMethod { impl parser::ApiMethod {
pub fn structs(&self) -> TokenStream {
let objects = self.types.objects();
let structs = objects.iter().map(|obj| obj.generate_struct());
quote! {
#(#structs)*
}
}
pub fn enums(&self) -> TokenStream {
let enums = self.types.enums();
let generated_enums = enums.iter().map(|e| e.generate());
quote! {
#(#generated_enums)*
}
}
pub fn name_snake(&self) -> Ident { pub fn name_snake(&self) -> Ident {
util::to_ident(&self.name.to_snake()) util::to_ident(&self.name.to_snake())
} }

View File

@ -3,13 +3,14 @@ use case::CaseExt;
use proc_macro2::{Ident, TokenStream}; use proc_macro2::{Ident, TokenStream};
use quote::quote; use quote::quote;
use super::util; use super::{api_group::GroupGeneration, util};
pub fn generate_groups(groups: Vec<parser::ApiGroup>) -> TokenStream { pub fn generate_groups(groups: Vec<parser::ApiGroup>, resp_derives: Vec<String>) -> TokenStream {
let gr = groups let gr = groups
.iter() .into_iter()
// implemented manually // implemented manually
.filter(|group| group.name != "authentication") .filter(|group| group.name != "authentication")
.map(|group| GroupGeneration::new(group, resp_derives.clone()))
.map(generate_group); .map(generate_group);
quote! { quote! {
@ -17,7 +18,7 @@ pub fn generate_groups(groups: Vec<parser::ApiGroup>) -> TokenStream {
} }
} }
fn generate_group(group: &parser::ApiGroup) -> TokenStream { fn generate_group(group: GroupGeneration) -> TokenStream {
let group = group.generate(); let group = group.generate();
quote! { quote! {
@ -25,13 +26,28 @@ fn generate_group(group: &parser::ApiGroup) -> TokenStream {
} }
} }
impl parser::TypeWithName { #[derive(Debug)]
pub struct StructGenerator<'a> {
type_: &'a parser::TypeWithName,
group: &'a GroupGeneration,
}
impl<'a> StructGenerator<'a> {
pub fn new(type_: &'a parser::TypeWithName, group: &'a GroupGeneration) -> Self {
Self { type_, group }
}
pub fn generate_struct(&self) -> TokenStream { pub fn generate_struct(&self) -> TokenStream {
let fields = self.types.iter().map(|obj| obj.generate_struct_field()); let fields = self
let name = util::to_ident(&self.name); .type_
.types
.iter()
.map(|obj| obj.generate_struct_field());
let name = util::to_ident(&self.type_.name);
let derives = self.group.response_derives(vec![]);
quote! { quote! {
#[derive(Debug, serde::Deserialize)] #derives
pub struct #name { pub struct #name {
#(#fields,)* #(#fields,)*
} }
@ -90,14 +106,29 @@ impl types::Type {
} }
} }
impl parser::Enum { #[derive(Debug)]
pub struct EnumGeneration<'a> {
enum_: &'a parser::Enum,
group: &'a GroupGeneration,
}
impl<'a> EnumGeneration<'a> {
pub fn new(enum_: &'a parser::Enum, group: &'a GroupGeneration) -> Self {
Self { enum_, group }
}
pub fn generate(&self) -> TokenStream { pub fn generate(&self) -> TokenStream {
let values = self.values.iter().map(|enum_value| enum_value.generate()); let values = self
let name = util::to_ident(&self.name); .enum_
.values
.iter()
.map(|enum_value| enum_value.generate());
let name = util::to_ident(&self.enum_.name);
let derives = self.group.response_derives(vec!["PartialEq", "Eq"]);
quote! { quote! {
#[allow(clippy::enum_variant_names)] #[allow(clippy::enum_variant_names)]
#[derive(Debug, serde::Deserialize, PartialEq, Eq)] #derives
pub enum #name { pub enum #name {
#(#values,)* #(#values,)*
} }

View File

@ -2,23 +2,27 @@ use crate::parser;
use proc_macro2::{Ident, TokenStream}; use proc_macro2::{Ident, TokenStream};
use quote::quote; use quote::quote;
use super::util; use super::{
api_group::GroupGeneration,
group::{EnumGeneration, StructGenerator},
util,
};
#[derive(Debug)] #[derive(Debug)]
pub struct GroupMethod<'a> { pub struct GroupMethod<'a> {
group: &'a parser::ApiGroup, group: &'a GroupGeneration,
method: &'a parser::ApiMethod, method: &'a parser::ApiMethod,
} }
impl<'a> GroupMethod<'a> { impl<'a> GroupMethod<'a> {
pub fn new(group: &'a parser::ApiGroup, method: &'a parser::ApiMethod) -> Self { pub fn new(group: &'a GroupGeneration, method: &'a parser::ApiMethod) -> Self {
Self { group, method } Self { group, method }
} }
pub fn generate_method(&self) -> TokenStream { pub fn generate_method(&self) -> TokenStream {
let method_name = self.method.name_snake(); let method_name = self.method.name_snake();
let structs = self.method.structs(); let structs = self.structs();
let enums = self.method.enums(); let enums = self.enums();
let builder = self.generate_request_builder(); let builder = self.generate_request_builder();
let response_struct = self.generate_response_struct(); let response_struct = self.generate_response_struct();
let request_method = self.generate_request_method(); let request_method = self.generate_request_method();
@ -34,6 +38,29 @@ impl<'a> GroupMethod<'a> {
} }
} }
pub fn structs(&self) -> TokenStream {
let objects = self.method.types.objects();
let structs = objects
.iter()
.map(|obj| StructGenerator::new(obj, self.group).generate_struct());
quote! {
#(#structs)*
}
}
pub fn enums(&self) -> TokenStream {
let enums = self.method.types.enums();
let generated_enums = enums
.iter()
.map(|enum_| EnumGeneration::new(enum_, self.group))
.map(|e| e.generate());
quote! {
#(#generated_enums)*
}
}
fn generate_request_method(&self) -> TokenStream { fn generate_request_method(&self) -> TokenStream {
let method_name = self.method.name_snake(); let method_name = self.method.name_snake();
@ -89,8 +116,10 @@ impl<'a> GroupMethod<'a> {
.iter() .iter()
.map(|field| field.generate_struct_field()); .map(|field| field.generate_struct_field());
let derives = self.group.response_derives(vec![]);
quote! { quote! {
#[derive(Debug, serde::Deserialize)] #derives
pub struct Response { pub struct Response {
#(#struct_fields,)* #(#struct_fields,)*
} }
@ -139,7 +168,7 @@ impl<'a> GroupMethod<'a> {
form_access: TokenStream, form_access: TokenStream,
form_factory: TokenStream, form_factory: TokenStream,
) -> TokenStream { ) -> TokenStream {
let method_url = format!("/api/v2/{}/{}", self.group.url, self.method.url); let method_url = format!("/api/v2/{}/{}", self.group.url(), self.method.url);
let (response_type, response_parse) = match self.method.types.response() { let (response_type, response_parse) = match self.method.types.response() {
Some(resp) => { Some(resp) => {

View File

@ -15,12 +15,13 @@ use self::{group::generate_groups, skeleton::generate_skeleton};
pub fn generate(ast: &syn::DeriveInput, api_content: &str) -> TokenStream { pub fn generate(ast: &syn::DeriveInput, api_content: &str) -> TokenStream {
let ident = &ast.ident; let ident = &ast.ident;
let resp_derives = get_response_derives(ast);
let token_tree = md_parser::TokenTreeFactory::create(api_content); let token_tree = md_parser::TokenTreeFactory::create(api_content);
let api_groups = parser::parse_api_groups(token_tree); let api_groups = parser::parse_api_groups(token_tree);
let skeleton = generate_skeleton(ident); let skeleton = generate_skeleton(ident);
let groups = generate_groups(api_groups); let groups = generate_groups(api_groups, resp_derives);
let impl_ident = syn::Ident::new(&format!("{}_impl", ident).to_snake(), ident.span()); let impl_ident = syn::Ident::new(&format!("{}_impl", ident).to_snake(), ident.span());
quote! { quote! {
@ -30,3 +31,34 @@ pub fn generate(ast: &syn::DeriveInput, api_content: &str) -> TokenStream {
} }
} }
} }
fn get_response_derives(ast: &syn::DeriveInput) -> Vec<String> {
ast.attrs
.iter()
.find(|attr| {
attr.path
.get_ident()
.map(|ident| ident == "api_gen")
.unwrap_or(false)
})
.into_iter()
.flat_map(|attr| attr.parse_meta().ok())
.filter_map(|meta| match meta {
syn::Meta::List(list) => Some(list.nested),
_ => None,
})
.flat_map(|nested| nested.into_iter())
.filter_map(|value| match value {
syn::NestedMeta::Meta(meta) => Some(meta),
_ => None,
})
.filter_map(|value| match value {
syn::Meta::NameValue(name_value) => Some(name_value.lit),
_ => None,
})
.filter_map(|lit| match lit {
syn::Lit::Str(str) => Some(str.value().split(',').map(|s| s.trim()).collect()),
_ => None,
})
.collect()
}