structfromdir/
lib.rs

1use proc_macro::TokenStream;
2use syn::{Data, DeriveInput, Fields, parse_macro_input};
3
4#[proc_macro_derive(FromDir)]
5pub fn from_dir(input: TokenStream) -> TokenStream {
6    let input = parse_macro_input!(input as DeriveInput);
7
8    let ident = input.ident;
9    let fields = match input.data {
10        Data::Struct(data) => match data.fields {
11            Fields::Named(fields) => fields,
12            _ => panic!("FromDir only supports named fields"),
13        },
14        _ => panic!("FromDir only supports structs"),
15    };
16
17    let field_idents = fields
18        .named
19        .iter()
20        .map(|f| {
21            f.ident
22                .as_ref()
23                .expect("Named field should have an identifier")
24        })
25        .collect::<Vec<_>>();
26
27    let field_types = fields.named.iter().map(|f| &f.ty).collect::<Vec<_>>();
28
29    let expanded = quote::quote! {
30        #[automatically_derived]
31        impl #ident {
32            pub fn from_dir(dir: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
33                use std::fs;
34                use std::io::Read;
35                use std::str::FromStr;
36                use std::any::TypeId;
37
38                let dir = dir.as_ref();
39                let string_type_id = TypeId::of::<String>();
40
41                #(
42                    let mut #field_idents: #field_types = {
43                        let mut data = fs::read_to_string(dir.join(stringify!(#field_idents)))?;
44                        let data = if TypeId::of::<#field_types>() == string_type_id {
45                            &data
46                        } else {
47                            data.trim()
48                        };
49                        #field_types::from_str(data).expect("TODO")
50                    };
51                )*
52
53                Ok(Self {
54                    #(#field_idents),*
55                })
56            }
57        }
58    };
59
60    TokenStream::from(expanded)
61}