2023-07-16 12:01:48 +00:00
|
|
|
mod mark_as_ci_command;
|
|
|
|
mod struct_to_ci_enum;
|
|
|
|
|
|
|
|
use mark_as_ci_command::generate_final_function;
|
2023-07-12 19:48:51 +00:00
|
|
|
use proc_macro::TokenStream;
|
2023-07-20 19:43:41 +00:00
|
|
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
|
|
|
use quote::{format_ident, quote};
|
|
|
|
use struct_to_ci_enum::{generate_command_enum, generate_generate_ci_function, generate_help_function};
|
|
|
|
use syn::{self, parse_quote, parse_str, DeriveInput, FieldMutability, ItemFn, Token, Visibility};
|
2023-07-12 19:48:51 +00:00
|
|
|
|
|
|
|
#[proc_macro_attribute]
|
2023-07-20 19:43:41 +00:00
|
|
|
pub fn turn_struct_to_ci_command_enum(_attrs: TokenStream, input: TokenStream) -> TokenStream {
|
2023-07-12 19:48:51 +00:00
|
|
|
// Construct a representation of Rust code as a syntax tree
|
|
|
|
// that we can manipulate
|
2023-07-20 19:43:41 +00:00
|
|
|
let mut input: DeriveInput = syn::parse(input).expect("This should always be valid rust code, as it's extracted from direct code");
|
|
|
|
|
|
|
|
let mut named_fields = match &input.data {
|
|
|
|
syn::Data::Struct(input) => match &input.fields {
|
|
|
|
syn::Fields::Named(named_fields) => named_fields,
|
|
|
|
_ => unimplemented!("The macro only works for named fields (e.g.: `Name: Type`)"),
|
|
|
|
},
|
|
|
|
_ => unimplemented!("The macro only works for structs"),
|
|
|
|
}
|
|
|
|
.to_owned();
|
|
|
|
|
|
|
|
let attr_parsed = parse_quote! {
|
|
|
|
/// This is a help function
|
|
|
|
};
|
|
|
|
|
|
|
|
named_fields.named.push(syn::Field {
|
|
|
|
attrs: vec![attr_parsed],
|
|
|
|
// attrs: attr_parser
|
|
|
|
// .parse("#[doc = r\"This is a help function\"]".to_token_stream().into())
|
|
|
|
// .expect("See reason for other one"),
|
|
|
|
vis: Visibility::Inherited,
|
|
|
|
mutability: FieldMutability::None,
|
|
|
|
ident: Some(format_ident!("help")),
|
|
|
|
colon_token: Some(Token![:](Span::call_site())),
|
|
|
|
ty: parse_str("fn(Option<String>) -> String").expect("This is static and valid rust code"),
|
|
|
|
});
|
|
|
|
|
|
|
|
match &mut input.data {
|
|
|
|
syn::Data::Struct(input) => input.fields = syn::Fields::Named(named_fields.clone()),
|
|
|
|
_ => unreachable!("This was a DataStruct before"),
|
|
|
|
};
|
2023-07-12 19:48:51 +00:00
|
|
|
|
|
|
|
// Build the trait implementation
|
2023-07-16 12:01:48 +00:00
|
|
|
let generate_ci_function: TokenStream2 = generate_generate_ci_function(&input);
|
|
|
|
|
2023-07-20 19:43:41 +00:00
|
|
|
let command_enum = generate_command_enum(&named_fields);
|
|
|
|
|
|
|
|
let help_function = generate_help_function(&named_fields);
|
2023-07-16 12:01:48 +00:00
|
|
|
|
|
|
|
quote! {
|
|
|
|
#command_enum
|
|
|
|
|
2023-07-20 19:43:41 +00:00
|
|
|
#generate_ci_function
|
2023-07-16 12:01:48 +00:00
|
|
|
|
2023-07-20 19:43:41 +00:00
|
|
|
//#help_function
|
2023-07-16 12:01:48 +00:00
|
|
|
}
|
|
|
|
.into()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[proc_macro_attribute]
|
|
|
|
pub fn ci_command(_attrs: TokenStream, input: TokenStream) -> TokenStream {
|
2023-07-20 19:43:41 +00:00
|
|
|
let mut input: ItemFn = syn::parse(input).expect("This should always be valid rust code, as it's extracted from direct code");
|
|
|
|
let output_function = generate_final_function(&mut input);
|
|
|
|
output_function.into()
|
2023-07-12 19:48:51 +00:00
|
|
|
}
|