resource_bound_derive/
lib.rs

1
2mod outer_attr;
3
4use proc_macro::{TokenStream};
5use quote::quote;
6use syn::{DeriveInput, Fields, spanned::Spanned};
7
8use outer_attr::OuterAttributes;
9
10
11/// Enforces compile-time resource constraints on structs.
12///
13/// See the crate README for details and examples.
14
15#[proc_macro_derive(ResourceBound, attributes(size_limit, allow_heap))]
16pub fn resource_derive(input: TokenStream) -> TokenStream {
17    let input = syn::parse_macro_input!(input as DeriveInput);
18
19    match expand_resource(&input) {
20        Ok(ts) => ts,
21        Err(e) => e.to_compile_error().into(),
22    }
23}
24
25fn expand_resource(input: &DeriveInput) -> syn::Result<TokenStream> {
26    let name = &input.ident;
27    let fields;
28    // 1️⃣ Validate target
29    match &input.data {
30        syn::Data::Struct(ds) => {fields=&ds.fields}
31        syn::Data::Enum(_) => {
32            return Err(syn::Error::new_spanned(
33                input,
34                "ResourceBound cannot be derived for enums",
35            ));
36        }
37        syn::Data::Union(_) => {
38            return Err(syn::Error::new_spanned(
39                input,
40                "ResourceBound cannot be derived for unions",
41            ));
42        }
43    }
44
45    //lifetimes
46    let lifetimes=input.generics.lifetimes();
47    let liftimes_use=input.generics.lifetimes();
48    
49    // 2️⃣ Parse outer attributes
50    let mut outer_attrs = OuterAttributes::new();
51    for attr in &input.attrs {
52        outer_attr::parse_struct_attrs(&mut outer_attrs, attr)?;
53    }
54
55
56    //extract data
57    let size_limit=match outer_attrs.get_max_size() {
58        Some(s)=>s,
59        None=>0    
60    };
61    let heap_permission=outer_attrs.get_heap_allo();//false by default if not explicitly defined
62    if !heap_permission{
63        map_fields(&fields,&mut outer_attrs)?;
64    }
65    let mut field_ctr=0;
66    let field_types=outer_attrs.get_types();
67    let field_assert=field_types.iter().map(move |ty|{
68        field_ctr+=1;
69        let ctr_name=String::from("FIELD")+&field_ctr.to_string();
70        let ctr_name=syn::Ident::new(&ctr_name,ty.span());
71        let assert_fn_name=String::from("assert_fn")+&field_ctr.to_string()[..];
72        let assert_fn_name=syn::Ident::new(&assert_fn_name[..], ty.span());
73        quote! {
74            
75                const #ctr_name : () ={
76                    const fn #assert_fn_name<T:StackOnly>(){};
77                    #assert_fn_name::<#ty>();
78                    ()
79                };
80        }
81    });
82
83    let allowed_heap_types_code=quote! {
84        use resource_bound_core::StackOnly;
85       
86        
87        
88    };
89    let heap_assert_code=if !heap_permission {
90        quote! {
91           #allowed_heap_types_code
92           #(#field_assert)* 
93        }
94    }else{
95        quote! {
96            
97        }
98    };
99    let expanded_limit=quote! {
100    
101        #heap_assert_code
102        impl<#(#lifetimes)*> #name <#(#liftimes_use)*> {
103            #[doc(hidden)]
104            const __RESOURCE_BOUND_ASSERTION_FAILED_BECAUSE_THE_STRUCT_SIZE_IS_MORE_THAN_THE_EXPLICITLY_DEFINED_VALUE: [(); 0] =
105                [(); (core::mem::size_of::<Self>() <= #size_limit) as usize - 1];
106        }
107
108    };
109    // 3️⃣ Generate code
110    Ok(expanded_limit
111    .into())
112}
113
114
115fn map_fields(fields:&Fields,mutable:&mut OuterAttributes)->syn::Result<()>{
116    
117    for field in fields{
118        let ty=&field.ty;
119        mutable.push_type(ty.clone());
120    }
121
122
123    Ok(())
124}