embedded_resources/
lib.rs1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::{format_ident, quote, ToTokens};
4use syn::{Attribute, Ident, ItemStruct, ItemType, Meta, Type, Visibility};
5
6fn generate_alias_stmt(
7 vis: &Visibility,
8 alias_value: &impl ToTokens,
9 alias_type: &impl ToTokens,
10) -> ItemType {
11 syn::parse2(quote! { #vis type #alias_value = #alias_type; }).unwrap()
12}
13
14#[proc_macro_attribute]
64pub fn resource_group(args: TokenStream, item: TokenStream) -> TokenStream {
65 let mut s: ItemStruct = syn::parse2(item.into()).expect("Resource item must be a struct.");
66
67 let attr: Option<Ident> = syn::parse2(args.into()).unwrap();
68
69 let generate_aliases = match attr {
70 None => true,
71 Some(ident) => {
72 assert_eq!(
73 ident.to_string(),
74 "no_aliases",
75 "Expected identifier \"no_aliases\"."
76 );
77 false
78 }
79 };
80
81 let vis = s.vis.clone();
82
83 s.fields
85 .iter_mut()
86 .for_each(|field| field.vis = vis.clone());
87
88 let mut aliases = Vec::new();
89
90 s.fields.iter_mut().for_each(|field| {
92 let mut custom_alias = false;
93
94 field.attrs = field
95 .attrs
96 .iter()
97 .cloned()
98 .filter(|attr| {
99 if let Meta::NameValue(alias) = &attr.meta {
100 if let Some(ident) = alias.path.get_ident() {
101 if ident.to_string().eq("alias") {
102 aliases.push(generate_alias_stmt(&vis, &alias.value, &field.ty));
103 custom_alias = true;
104 return false;
105 }
106 }
107 }
108
109 true
110 })
111 .collect();
112
113 if generate_aliases && !custom_alias {
114 aliases.push(generate_alias_stmt(
115 &vis,
116 &format_ident!(
117 "{}",
118 inflector::cases::classcase::to_class_case(
119 field.ident.as_ref().unwrap().to_string().as_str()
120 )
121 ),
122 &field.ty,
123 ));
124 }
125 });
126
127 let use_macro_ident = Ident::new(
128 inflector::cases::snakecase::to_snake_case(s.ident.to_string().as_str()).as_str(),
129 Span::call_site(),
130 );
131 let macro_vis = if let Visibility::Restricted(_) = vis {
132 Some(quote! { #vis use #use_macro_ident; })
133 } else {
134 None
135 };
136
137 let ident = &s.ident;
138 let field_idents: Vec<Ident> = s
139 .fields
140 .iter()
141 .cloned()
142 .map(|field| field.ident.unwrap())
143 .collect();
144 let field_types: Vec<Type> = s
145 .fields
146 .iter()
147 .cloned()
148 .map(|field| {
149 if let Type::Path(ty) = field.ty {
150 let ident = &ty.path.segments.last().unwrap().ident;
151 syn::parse2(quote! { #ident }).unwrap()
152 } else {
153 field.ty
154 }
155 })
156 .collect();
157 let field_attrs: Vec<Vec<Attribute>> =
158 s.fields.iter().cloned().map(|field| field.attrs).collect();
159 let doc = format!(
160 "Extract `{}` from a `Peripherals` instance.",
161 ident.to_string()
162 );
163
164 quote! {
165 #(
166 #aliases
167 )*
168
169 #s
170
171 #[doc = #doc]
172 macro_rules! #use_macro_ident {
173 ( $P:ident ) => {
174 #ident {
175 #(
176 #(
177 #field_attrs
178 )*
179 #field_idents: $P.#field_types
180 ),*
181 }
182 };
183 }
184
185 #macro_vis
186 }
187 .into()
188}