derive_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput};
4
5fn generate_impl(
6    struct_name: &syn::Ident,
7    trait_name: &str,
8    ext_name: &str,
9) -> proc_macro2::TokenStream {
10    let format_mod = syn::Ident::new(ext_name, proc_macro2::Span::call_site());
11    let trait_ident = syn::Ident::new(trait_name, proc_macro2::Span::call_site());
12    let to_string_fn = syn::Ident::new(
13        if ext_name == "yaml" {
14            "to_string"
15        } else {
16            "to_string_pretty"
17        },
18        proc_macro2::Span::call_site(),
19    );
20
21    let path_method = if cfg!(feature = "dirs") {
22        quote! {
23            fn path() -> Result<std::path::PathBuf, derive_config::ConfigError> {
24                let path = derive_config::dirs::config_dir().ok_or(derive_config::ConfigError::None)?;
25                let name = env!("CARGO_PKG_NAME");
26                let file = format!("{}.{}", name, #ext_name);
27
28                Ok(path.join(file))
29            }
30        }
31    } else {
32        quote! {
33            fn path() -> Result<std::path::PathBuf, derive_config::ConfigError> {
34                let mut path = std::env::current_exe()?;
35                path.set_file_name(env!("CARGO_CRATE_NAME"));
36                path.set_extension(#ext_name);
37
38                Ok(path)
39            }
40        }
41    };
42
43    quote! {
44        impl #trait_ident for #struct_name {
45            #path_method
46
47            fn save(&self) -> Result<(), derive_config::ConfigError> {
48                use std::io::Write;
49
50                let path = Self::path()?;
51                let mut file = std::fs::File::options()
52                    .write(true)
53                    .create(true)
54                    .truncate(true)
55                    .open(path)?;
56
57                let content = derive_config::#format_mod::#to_string_fn(&self)?;
58                file.write_all(content.as_bytes())?;
59
60                Ok(())
61            }
62
63            fn load() -> Result<Self, derive_config::ConfigError> {
64                use std::io::{Read, Seek};
65
66                let path = Self::path()?;
67                let mut file = std::fs::File::open(&path)?;
68                let mut text = String::new();
69                file.read_to_string(&mut text)?;
70                file.rewind()?;
71
72                let config = derive_config::#format_mod::from_str(&text)?;
73
74                Ok(config)
75            }
76        }
77    }
78}
79
80fn derive_config(input: TokenStream, trait_name: &str, ext_name: &str) -> TokenStream {
81    let input = parse_macro_input!(input as DeriveInput);
82    let struct_name = input.ident;
83
84    let expanded = generate_impl(&struct_name, trait_name, ext_name);
85
86    TokenStream::from(expanded)
87}
88
89#[cfg(feature = "json")]
90#[proc_macro_derive(DeriveJsonConfig)]
91pub fn derive_json_config(input: TokenStream) -> TokenStream {
92    derive_config(input, "DeriveJsonConfig", "json")
93}
94
95#[cfg(feature = "toml")]
96#[proc_macro_derive(DeriveTomlConfig)]
97pub fn derive_toml_config(input: TokenStream) -> TokenStream {
98    derive_config(input, "DeriveTomlConfig", "toml")
99}
100
101#[cfg(feature = "yaml")]
102#[proc_macro_derive(DeriveYamlConfig)]
103pub fn derive_yaml_config(input: TokenStream) -> TokenStream {
104    derive_config(input, "DeriveYamlConfig", "yaml")
105}