Skip to main content

rustview_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, ItemFn};
4
5/// Marks a function as a RustView application entry point.
6///
7/// The function must accept `&mut Ui` as its only parameter.
8///
9/// # Example
10/// ```ignore
11/// #[rustview::app]
12/// fn my_app(ui: &mut Ui) {
13///     ui.write("Hello, world!");
14/// }
15/// ```
16#[proc_macro_attribute]
17pub fn app(_attr: TokenStream, item: TokenStream) -> TokenStream {
18    let input = parse_macro_input!(item as ItemFn);
19    let fn_name = &input.sig.ident;
20    let fn_block = &input.block;
21    let fn_vis = &input.vis;
22
23    let expanded = quote! {
24        #fn_vis fn #fn_name(ui: &mut rustview::ui::Ui) {
25            #fn_block
26        }
27    };
28
29    TokenStream::from(expanded)
30}
31
32/// Caches the return value of a function based on its arguments.
33///
34/// The cache is keyed on function name + serialized arguments using FNV-1a hashing.
35/// Cache is per-process, shared across sessions.
36/// Maximum 128 entries per function with LRU eviction (configurable).
37///
38/// # Requirements
39/// - Arguments must implement `Hash + Eq + Clone`
40/// - Return type must implement `Clone`
41///
42/// # Example
43/// ```ignore
44/// #[rustview::cached]
45/// fn expensive_computation(n: i64) -> Vec<f64> {
46///     (0..n).map(|i| (i as f64).sqrt()).collect()
47/// }
48/// ```
49#[proc_macro_attribute]
50pub fn cached(_attr: TokenStream, item: TokenStream) -> TokenStream {
51    let input = parse_macro_input!(item as ItemFn);
52    let fn_name = &input.sig.ident;
53    let fn_vis = &input.vis;
54    let fn_sig = &input.sig;
55    let fn_block = &input.block;
56    let fn_output = &fn_sig.output;
57    let fn_inputs = &fn_sig.inputs;
58
59    let fn_name_str = fn_name.to_string();
60
61    // Extract parameter names for cache key construction
62    let param_names: Vec<_> = fn_inputs
63        .iter()
64        .filter_map(|arg| {
65            if let syn::FnArg::Typed(pat_type) = arg {
66                if let syn::Pat::Ident(pat_ident) = pat_type.pat.as_ref() {
67                    return Some(pat_ident.ident.clone());
68                }
69            }
70            None
71        })
72        .collect();
73
74    let expanded = quote! {
75        #fn_vis fn #fn_name(#fn_inputs) #fn_output {
76            use std::hash::{Hash, Hasher};
77
78            let mut hasher = fnv::FnvHasher::default();
79            #fn_name_str.hash(&mut hasher);
80            #( #param_names.hash(&mut hasher); )*
81            let cache_key = hasher.finish();
82
83            // Check global cache
84            if let Some(cached) = rustview::cache::get_cached::<_>(#fn_name_str, cache_key) {
85                return cached;
86            }
87
88            let result = (|| #fn_block)();
89
90            rustview::cache::insert_cached(#fn_name_str, cache_key, result.clone());
91
92            result
93        }
94    };
95
96    TokenStream::from(expanded)
97}