gh_workflow_macros/
lib.rs

1use heck::ToSnakeCase;
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::{parse_macro_input, Data, DeriveInput, Fields};
5
6#[proc_macro_derive(Context)]
7pub fn derive_expr(input: TokenStream) -> TokenStream {
8    let input = parse_macro_input!(input as DeriveInput);
9    let struct_name = input.ident;
10    let ctor_name = struct_name.to_string().to_snake_case();
11    let ctor_id = syn::Ident::new(&ctor_name, struct_name.span());
12
13    // Ensure it's a struct and get its fields
14    let fields = if let Data::Struct(data_struct) = input.data {
15        if let Fields::Named(fields) = data_struct.fields {
16            fields
17        } else {
18            panic!("#[derive(Context)] only supports structs with named fields")
19        }
20    } else {
21        panic!("#[derive(Context)] can only be used with structs");
22    };
23
24    // Generate methods for each field
25    let methods = fields.named.iter().map(|field| {
26        let field_name = &field.ident;
27        let field_type = &field.ty;
28        let field_name_str = field_name.as_ref().unwrap().to_string();
29        quote! {
30            pub fn #field_name(&self) -> Context<#field_type> {
31                self.select::<#field_type>(#field_name_str)
32            }
33        }
34    });
35
36    // Generate the output code
37    let expanded = quote! {
38        impl Context<#struct_name> {
39            #(#methods)*
40
41            pub fn #ctor_id() -> Self {
42                Context::<Github>::new().select(stringify!(#ctor_name))
43            }
44        }
45    };
46
47    // eprintln!("Generated code:\n{}", expanded);
48
49    TokenStream::from(expanded)
50}