65 lines
1.8 KiB
Rust
65 lines
1.8 KiB
Rust
use crate::helpers::clean_rust_type_syntax;
|
|
use proc_macro2::{Span, TokenStream};
|
|
use quote::{ToTokens, format_ident, quote};
|
|
use syn::{Data, DeriveInput, Fields, Type, parse2};
|
|
|
|
pub fn derive_extract_field_impl(input: TokenStream) -> syn::Result<TokenStream> {
|
|
let input = parse2::<DeriveInput>(input)?;
|
|
let struct_name = &input.ident;
|
|
let generics = &input.generics;
|
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
|
|
|
let line_number = struct_name.span().start().line;
|
|
|
|
let fields = match &input.data {
|
|
Data::Struct(data) => match &data.fields {
|
|
Fields::Named(fields) => &fields.named,
|
|
_ => return Err(syn::Error::new(Span::call_site(), "ExtractField only works on structs with named fields")),
|
|
},
|
|
_ => return Err(syn::Error::new(Span::call_site(), "ExtractField only works on structs")),
|
|
};
|
|
|
|
let mut field_line = Vec::new();
|
|
// Extract field names and types as strings at compile time
|
|
let field_info = fields
|
|
.iter()
|
|
.map(|field| {
|
|
let ident = field.ident.as_ref().unwrap();
|
|
let name = ident.to_string();
|
|
let ty = clean_rust_type_syntax(field.ty.to_token_stream().to_string());
|
|
let line = ident.span().start().line;
|
|
field_line.push(line);
|
|
(name, ty)
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
let field_str = field_info.into_iter().map(|(name, ty)| (format!("{name}: {ty}")));
|
|
|
|
let res = quote! {
|
|
impl #impl_generics ExtractField for #struct_name #ty_generics #where_clause {
|
|
fn field_types() -> Vec<(String, usize)> {
|
|
vec![
|
|
#((String::from(#field_str), #field_line)),*
|
|
]
|
|
}
|
|
|
|
fn print_field_types() {
|
|
for (field, line) in Self::field_types() {
|
|
println!("{} at line {}", field, line);
|
|
}
|
|
}
|
|
|
|
fn path() -> &'static str {
|
|
file!()
|
|
}
|
|
|
|
fn line_number() -> usize {
|
|
#line_number
|
|
}
|
|
|
|
}
|
|
};
|
|
|
|
Ok(res)
|
|
}
|