1use 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 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}