sp_api_proc_macro/
runtime_metadata.rs1use proc_macro2::TokenStream as TokenStream2;
19use quote::quote;
20use syn::{parse_quote, ItemImpl, ItemTrait, Result};
21
22use crate::{
23 common::CHANGED_IN_ATTRIBUTE,
24 utils::{
25 extract_impl_trait, filter_cfg_attributes, generate_crate_access,
26 generate_runtime_mod_name_for_trait, get_doc_literals, RequireQualifiedTraitPath,
27 },
28};
29
30fn get_type_param(ty: &syn::Type) -> syn::Type {
46 let ty_elem = match &ty {
49 syn::Type::Reference(reference) => &reference.elem,
50 syn::Type::Ptr(ptr) => &ptr.elem,
51 syn::Type::Slice(slice) => &slice.elem,
52 syn::Type::Array(arr) => &arr.elem,
53 _ => ty,
54 };
55
56 ty_elem.clone()
57}
58
59fn collect_docs(attrs: &[syn::Attribute], crate_: &TokenStream2) -> TokenStream2 {
63 if cfg!(feature = "no-metadata-docs") {
64 quote!(#crate_::vec![])
65 } else {
66 let docs = get_doc_literals(&attrs);
67 quote!(#crate_::vec![ #( #docs, )* ])
68 }
69}
70
71pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 {
76 let crate_ = generate_crate_access();
77 let mut methods = Vec::new();
78
79 let mut where_clause = Vec::new();
89 for item in &decl.items {
90 let syn::TraitItem::Fn(method) = item else { continue };
92
93 let is_changed_in =
95 method.attrs.iter().any(|attr| attr.path().is_ident(CHANGED_IN_ATTRIBUTE));
96 if is_changed_in {
97 continue;
98 }
99
100 let mut inputs = Vec::new();
101 let signature = &method.sig;
102 for input in &signature.inputs {
103 let syn::FnArg::Typed(typed) = input else { continue };
105
106 let pat = &typed.pat;
107 let name = quote!(#pat).to_string();
108 let ty = &typed.ty;
109
110 where_clause.push(get_type_param(ty));
111
112 inputs.push(quote!(
113 #crate_::metadata_ir::RuntimeApiMethodParamMetadataIR {
114 name: #name,
115 ty: #crate_::scale_info::meta_type::<#ty>(),
116 }
117 ));
118 }
119
120 let output = match &signature.output {
121 syn::ReturnType::Default => quote!(#crate_::scale_info::meta_type::<()>()),
122 syn::ReturnType::Type(_, ty) => {
123 where_clause.push(get_type_param(ty));
124 quote!(#crate_::scale_info::meta_type::<#ty>())
125 },
126 };
127
128 let method_name = signature.ident.to_string();
130 let docs = collect_docs(&method.attrs, &crate_);
131
132 let attrs = filter_cfg_attributes(&method.attrs);
134 let deprecation = match crate::utils::get_deprecation(&crate_, &method.attrs) {
135 Ok(deprecation) => deprecation,
136 Err(e) => return e.into_compile_error(),
137 };
138 methods.push(quote!(
139 #( #attrs )*
140 #crate_::metadata_ir::RuntimeApiMethodMetadataIR {
141 name: #method_name,
142 inputs: #crate_::vec![ #( #inputs, )* ],
143 output: #output,
144 docs: #docs,
145 deprecation_info: #deprecation,
146 }
147 ));
148 }
149
150 let trait_name_ident = &decl.ident;
151 let trait_name = trait_name_ident.to_string();
152 let docs = collect_docs(&decl.attrs, &crate_);
153 let deprecation = match crate::utils::get_deprecation(&crate_, &decl.attrs) {
154 Ok(deprecation) => deprecation,
155 Err(e) => return e.into_compile_error(),
156 };
157 let attrs = filter_cfg_attributes(&decl.attrs);
158 let mut generics = decl.generics.clone();
160 for generic_param in generics.params.iter_mut() {
161 let syn::GenericParam::Type(ty) = generic_param else { continue };
162
163 ty.eq_token = None;
165 ty.default = None;
166 }
167
168 where_clause
169 .into_iter()
170 .map(|ty| parse_quote!(#ty: #crate_::scale_info::TypeInfo + 'static))
171 .for_each(|w| generics.make_where_clause().predicates.push(w));
172
173 let (impl_generics, _, where_clause) = generics.split_for_impl();
174
175 quote!(
176 #crate_::frame_metadata_enabled! {
177 #( #attrs )*
178 #[inline(always)]
179 pub fn runtime_metadata #impl_generics () -> #crate_::metadata_ir::RuntimeApiMetadataIR
180 #where_clause
181 {
182 #crate_::metadata_ir::RuntimeApiMetadataIR {
183 name: #trait_name,
184 methods: #crate_::vec![ #( #methods, )* ],
185 docs: #docs,
186 deprecation_info: #deprecation,
187 }
188 }
189 }
190 )
191}
192
193pub fn generate_impl_runtime_metadata(impls: &[ItemImpl]) -> Result<TokenStream2> {
199 if impls.is_empty() {
200 return Ok(quote!());
201 }
202
203 let crate_ = generate_crate_access();
204
205 let runtime_name = &impls
207 .get(0)
208 .expect("Traits should contain at least one implementation; qed")
209 .self_ty;
210
211 let mut metadata = Vec::new();
212
213 for impl_ in impls {
214 let mut trait_ = extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?.clone();
215
216 let trait_name_ident = &trait_
219 .segments
220 .last()
221 .as_ref()
222 .expect("Trait path should always contain at least one item; qed")
223 .ident;
224
225 let generics = trait_
228 .segments
229 .iter()
230 .find_map(|segment| {
231 if let syn::PathArguments::AngleBracketed(generics) = &segment.arguments {
232 Some(generics.clone())
233 } else {
234 None
235 }
236 })
237 .expect("Trait path should always contain at least one generic parameter; qed");
238
239 let mod_name = generate_runtime_mod_name_for_trait(&trait_name_ident);
240 if let Some(segment) = trait_.segments.last_mut() {
242 *segment = parse_quote!(#mod_name);
243 }
244
245 let attrs = filter_cfg_attributes(&impl_.attrs);
246 metadata.push(quote!(
247 #( #attrs )*
248 #trait_::runtime_metadata::#generics()
249 ));
250 }
251
252 Ok(quote!(
270 #crate_::frame_metadata_enabled! {
271 #[doc(hidden)]
272 trait InternalImplRuntimeApis {
273 #[inline(always)]
274 fn runtime_metadata(&self) -> #crate_::vec::Vec<#crate_::metadata_ir::RuntimeApiMetadataIR> {
275 #crate_::vec![ #( #metadata, )* ]
276 }
277 }
278 #[doc(hidden)]
279 impl InternalImplRuntimeApis for #runtime_name {}
280 }
281 ))
282}