app_state_macros/lib.rs
1mod util;
2
3extern crate proc_macro;
4
5use crate::util::path::PathAttr;
6use crate::util::stateful::expand_stateful;
7use proc_macro::TokenStream as RawStream;
8use proc_macro2::Ident;
9use quote::quote;
10use rand::Rng;
11use std::env;
12use syn::spanned::Spanned;
13use syn::DeriveInput;
14
15/// Derive macro for `InitAppState`.
16/// Allows you to initialize app states with `init_app_state`.
17///
18/// # Example
19/// ```no_run
20/// use app_state::{stateful, AppState, InitAppState};
21///
22/// #[derive(InitAppState)]
23/// struct State {
24/// name: String,
25/// }
26///
27/// fn main() {
28/// State {
29/// name: "Hello".to_string(),
30/// }.init_app_state();
31/// }
32/// ```
33#[proc_macro_derive(InitAppState)]
34pub fn init_app_state(input: RawStream) -> RawStream {
35 let input = syn::parse_macro_input!(input as syn::DeriveInput);
36 let name = input.ident;
37
38 #[cfg(feature = "log")]
39 let log = quote! {
40 log::debug!("Initializing app state {}", std::any::type_name::<#name>());
41 };
42 #[cfg(not(feature = "log"))]
43 let log = quote! {};
44
45 let gen = quote! {
46 impl InitAppState for #name {
47 fn init_app_state(self) {
48 #log
49 AppState::init(self);
50 }
51 }
52 };
53 gen.into()
54}
55
56/// Derive macro for `InitMutAppState`.
57/// Allows you to initialize app states with `init_mut_app_state`.
58///
59/// # Example
60/// ```no_run
61/// use app_state::{stateful, MutAppState, InitMutAppState};
62///
63/// #[derive(InitMutAppState)]
64/// struct State {
65/// name: String,
66/// }
67///
68/// fn main() {
69/// State {
70/// name: "Hello".to_string(),
71/// }.init_mut_app_state();
72/// }
73/// ```
74#[proc_macro_derive(InitMutAppState)]
75pub fn init_mut_app_state(input: RawStream) -> RawStream {
76 let input = syn::parse_macro_input!(input as syn::DeriveInput);
77 let name = input.ident;
78
79 #[cfg(feature = "log")]
80 let log = quote! {
81 log::debug!("Initializing mutable app state {}", std::any::type_name::<#name>());
82 };
83 #[cfg(not(feature = "log"))]
84 let log = quote! {};
85
86 let gen = quote! {
87 impl InitMutAppState for #name {
88 fn init_mut_app_state(self) {
89 #log
90 MutAppState::init(self);
91 }
92 }
93 };
94 gen.into()
95}
96
97/// Inject app states into the annotated function.
98///
99/// # Arguments
100/// ## `init`
101/// A list of argument names to initialize to their default values
102/// if they are not already initialized. This requires
103/// the specified states to implement `Default`.
104///
105/// # Examples
106/// ## Injecting multiple states
107/// ```no_run
108/// use app_state::{AppState, MutAppState, stateful};
109///
110/// struct SomeState;
111/// struct SomeMutState;
112/// struct SomeOtherState;
113///
114/// #[stateful]
115/// fn foo(app_state: AppState<SomeState>,
116/// mut_app_state: MutAppState<SomeMutState>,
117/// mut other_state: MutAppStateLock<SomeOtherState>) {
118/// // ...
119/// }
120///
121/// fn main() {
122/// AppState::init(SomeState);
123/// MutAppState::init(SomeMutState);
124/// MutAppState::init(SomeOtherState);
125///
126/// foo();
127/// }
128/// ```
129///
130/// ## Injecting states with default values
131/// ```no_run
132/// use app_state::{AppState, MutAppState, stateful};
133///
134/// #[derive(Default)]
135/// struct SomeState;
136/// #[derive(Default)]
137/// struct SomeMutState;
138/// #[derive(Default)]
139/// struct SomeOtherState;
140///
141/// #[stateful(init(app_state, mut_app_state, other_state))]
142/// fn foo(app_state: AppState<SomeState>,
143/// mut_app_state: MutAppState<SomeMutState>,
144/// mut other_state: MutAppStateLock<SomeOtherState>) {
145/// // ...
146/// }
147///
148/// fn main() {
149/// // All states will be initialized with their default values
150/// // if they are not already initialized.
151/// foo();
152/// }
153/// ```
154#[proc_macro_attribute]
155pub fn stateful(args: RawStream, input: RawStream) -> RawStream {
156 let args = syn::parse_macro_input!(args as PathAttr);
157
158 match expand_stateful(input.into(), args) {
159 Ok(stream) => {
160 if env::var("DEBUG_GENERATED_CODE").is_ok() {
161 println!("{}", stream.to_string());
162 }
163
164 stream.into()
165 }
166 Err(err) => err.to_compile_error().into(),
167 }
168}
169
170fn get_default_state_values(input: DeriveInput) -> syn::Result<(Ident, Ident)> {
171 let name = input.ident.clone();
172
173 let mut rng = rand::thread_rng();
174 let id = Ident::new(
175 &format!("__init_default_state_{}", rng.gen::<u32>()),
176 proc_macro2::Span::call_site(),
177 );
178
179 if let syn::Item::Struct(_) = input.clone().into() {
180 Ok((name, id))
181 } else {
182 Err(syn::Error::new(
183 input.span(),
184 "'init_default_state' can only be used on structs",
185 ))
186 }
187}
188
189/// Initialize the default state of the annotated struct
190/// on application startup using `ctor`.
191/// The default state is the result of calling `Default::default()`.
192///
193/// # Example
194/// ```no_run
195/// use app_state::{MutAppState, init_default_state, AppStateTrait};
196///
197/// #[init_default_state]
198/// #[derive(Default)]
199/// struct SomeState {
200/// name: String,
201/// }
202/// ```
203#[proc_macro_attribute]
204pub fn init_default_state(_: RawStream, input: RawStream) -> RawStream {
205 let input = syn::parse_macro_input!(input as syn::DeriveInput);
206 let (name, id) = match get_default_state_values(input.clone()) {
207 Ok(res) => res,
208 Err(err) => return err.to_compile_error().into(),
209 };
210
211 #[cfg(feature = "log")]
212 let log = quote! {
213 log::debug!("Initializing app state {} before main", std::any::type_name::<#name>());
214 };
215 #[cfg(not(feature = "log"))]
216 let log = quote! {};
217
218 (quote! {
219 #input
220
221 #[ctor::ctor]
222 fn #id() {
223 #log
224 AppState::init(#name::default());
225 }
226 })
227 .into()
228}
229
230/// Initialize the default state of the annotated struct
231/// on application startup using `ctor`.
232/// The default state is the result of calling `Default::default()`.
233///
234/// # Example
235/// ```no_run
236/// use app_state::{MutAppState, init_default_mut_state, AppStateTrait};
237///
238/// #[init_default_mut_state]
239/// #[derive(Default)]
240/// struct SomeState {
241/// name: String,
242/// }
243/// ```
244#[proc_macro_attribute]
245pub fn init_default_mut_state(_: RawStream, input: RawStream) -> RawStream {
246 let input = syn::parse_macro_input!(input as syn::DeriveInput);
247 let (name, id) = match get_default_state_values(input.clone()) {
248 Ok(res) => res,
249 Err(err) => return err.to_compile_error().into(),
250 };
251
252 #[cfg(feature = "log")]
253 let log = quote! {
254 log::debug!("Initializing mutable app state {} before main", std::any::type_name::<#name>());
255 };
256 #[cfg(not(feature = "log"))]
257 let log = quote! {};
258
259 (quote! {
260 #input
261
262 #[ctor::ctor]
263 fn #id() {
264 #log
265 MutAppState::init(#name::default());
266 }
267 })
268 .into()
269}