save_load_derive/
lib.rs

1// ---------------- [ File: save-load-derive/src/lib.rs ]
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::{DeriveInput,parse_macro_input};
5use tracing::*;
6
7#[proc_macro_derive(SaveLoad)]
8pub fn save_load_derive(input: TokenStream) -> TokenStream {
9    trace!("Entered save_load_derive procedural macro.");
10
11    let ast = parse_macro_input!(input as DeriveInput);
12    let ident = &ast.ident;
13
14    debug!(
15        "Generating `SaveToFile` and `LoadFromFile` impl for `{}` using standard trait bounds.",
16        ident
17    );
18
19    // We generate code with T: Serialize + for<'de> Deserialize<'de> trait bounds.
20    // If the user hasn't derived or otherwise implemented these, the compiler
21    // will produce a standard error message about missing trait bounds instead.
22    //
23    // We also reference the traits and error type from `save_load_traits` using
24    // fully-qualified paths. Adjust them as necessary for your actual crate setup.
25    let expanded = quote! {
26
27        #[async_trait]
28        impl SaveToFile for #ident
29        where
30            #ident: ::serde::Serialize,
31        {
32            type Error = SaveLoadError;
33
34            async fn save_to_file(
35                &self,
36                filename: impl AsRef<std::path::Path> + Send
37            ) -> Result<(), Self::Error> {
38                tracing::debug!(
39                    "Attempting to save `{}` to file: {:?}",
40                    stringify!(#ident),
41                    filename.as_ref()
42                );
43
44                let serialized = serde_json::to_string_pretty(self)?;
45
46                tokio::fs::write(filename.as_ref(), &serialized).await?;
47
48                tracing::info!(
49                    "Successfully saved `{}` to file: {:?}",
50                    stringify!(#ident),
51                    filename.as_ref()
52                );
53                Ok(())
54            }
55        }
56
57        #[async_trait]
58        impl LoadFromFile for #ident
59        where
60            #ident: for<'de> ::serde::de::Deserialize<'de>,
61        {
62            type Error = SaveLoadError;
63
64            async fn load_from_file(
65                filename: impl AsRef<std::path::Path> + Send
66            ) -> Result<Self, Self::Error> {
67                tracing::debug!(
68                    "Attempting to load `{}` from file: {:?}",
69                    stringify!(#ident),
70                    filename.as_ref()
71                );
72
73                let content = tokio::fs::read_to_string(filename.as_ref()).await?;
74
75                Ok(serde_json::from_str(&content)?)
76            }
77        }
78    };
79
80    trace!(
81        "Completed macro expansions for `{}`. Returning generated code.",
82        ident
83    );
84    expanded.into()
85}