sp_api_proc_macro/
runtime_metadata.rs1use proc_macro2::TokenStream as TokenStream2;
19use quote::quote;
20use syn::{parse_quote, spanned::Spanned, ItemImpl, ItemTrait, Result};
21
22use crate::utils::{
23 extract_api_version, extract_impl_trait, filter_cfg_attributes, generate_crate_access,
24 generate_runtime_mod_name_for_trait, get_doc_literals, RequireQualifiedTraitPath,
25};
26
27fn get_type_param(ty: &syn::Type) -> syn::Type {
43 let ty_elem = match &ty {
46 syn::Type::Reference(reference) => &reference.elem,
47 syn::Type::Ptr(ptr) => &ptr.elem,
48 syn::Type::Slice(slice) => &slice.elem,
49 syn::Type::Array(arr) => &arr.elem,
50 _ => ty,
51 };
52
53 ty_elem.clone()
54}
55
56fn collect_docs(attrs: &[syn::Attribute], crate_: &TokenStream2) -> TokenStream2 {
60 if cfg!(feature = "no-metadata-docs") {
61 quote!(#crate_::vec![])
62 } else {
63 let docs = get_doc_literals(&attrs);
64 quote!(#crate_::vec![ #( #docs, )* ])
65 }
66}
67
68pub fn generate_decl_runtime_metadata<'a>(
73 decl: &ItemTrait,
74 versioned_methods_iter: impl Iterator<Item = (&'a syn::TraitItemFn, u32)>,
75) -> TokenStream2 {
76 let crate_ = generate_crate_access();
77 let mut methods = Vec::new();
78
79 let mut where_clause = Vec::new();
89 for (method, version) in versioned_methods_iter {
90 let mut inputs = Vec::new();
91 let signature = &method.sig;
92 for input in &signature.inputs {
93 let syn::FnArg::Typed(typed) = input else { continue };
95
96 let pat = &typed.pat;
97 let name = quote!(#pat).to_string();
98 let ty = &typed.ty;
99
100 where_clause.push(get_type_param(ty));
101
102 inputs.push(quote!(
103 #crate_::metadata_ir::RuntimeApiMethodParamMetadataIR {
104 name: #name,
105 ty: #crate_::scale_info::meta_type::<#ty>(),
106 }
107 ));
108 }
109
110 let output = match &signature.output {
111 syn::ReturnType::Default => quote!(#crate_::scale_info::meta_type::<()>()),
112 syn::ReturnType::Type(_, ty) => {
113 where_clause.push(get_type_param(ty));
114 quote!(#crate_::scale_info::meta_type::<#ty>())
115 },
116 };
117
118 let method_name = signature.ident.to_string();
120 let docs = collect_docs(&method.attrs, &crate_);
121
122 let attrs = filter_cfg_attributes(&method.attrs);
124 let deprecation = match crate::utils::get_deprecation(&crate_, &method.attrs) {
125 Ok(deprecation) => deprecation,
126 Err(e) => return e.into_compile_error(),
127 };
128
129 methods.push(quote!(
132 #( #attrs )*
133 if #version <= impl_version {
134 Some(#crate_::metadata_ir::RuntimeApiMethodMetadataIR {
135 name: #method_name,
136 inputs: #crate_::vec![ #( #inputs, )* ],
137 output: #output,
138 docs: #docs,
139 deprecation_info: #deprecation,
140 })
141 } else {
142 None
143 }
144 ));
145 }
146
147 let trait_name_ident = &decl.ident;
148 let trait_name = trait_name_ident.to_string();
149 let docs = collect_docs(&decl.attrs, &crate_);
150 let deprecation = match crate::utils::get_deprecation(&crate_, &decl.attrs) {
151 Ok(deprecation) => deprecation,
152 Err(e) => return e.into_compile_error(),
153 };
154 let attrs = filter_cfg_attributes(&decl.attrs);
155 let mut generics = decl.generics.clone();
157 for generic_param in generics.params.iter_mut() {
158 let syn::GenericParam::Type(ty) = generic_param else { continue };
159
160 ty.eq_token = None;
162 ty.default = None;
163 }
164
165 where_clause
166 .into_iter()
167 .map(|ty| parse_quote!(#ty: #crate_::scale_info::TypeInfo + 'static))
168 .for_each(|w| generics.make_where_clause().predicates.push(w));
169
170 let (impl_generics, _, where_clause) = generics.split_for_impl();
171
172 quote!(
173 #crate_::frame_metadata_enabled! {
174 #( #attrs )*
175 #[inline(always)]
176 pub fn runtime_metadata #impl_generics (impl_version: u32) -> #crate_::metadata_ir::RuntimeApiMetadataIR
177 #where_clause
178 {
179 #crate_::metadata_ir::RuntimeApiMetadataIR {
180 name: #trait_name,
181 methods: [ #( #methods, )* ]
182 .into_iter()
183 .filter_map(|maybe_m| maybe_m)
184 .collect(),
185 docs: #docs,
186 deprecation_info: #deprecation,
187 version: impl_version.into(),
188 }
189 }
190 }
191 )
192}
193
194pub fn generate_impl_runtime_metadata(impls: &[ItemImpl]) -> Result<TokenStream2> {
200 if impls.is_empty() {
201 return Ok(quote!());
202 }
203
204 let crate_ = generate_crate_access();
205
206 let runtime_name = &impls
208 .get(0)
209 .expect("Traits should contain at least one implementation; qed")
210 .self_ty;
211
212 let mut metadata = Vec::new();
213
214 for impl_ in impls {
215 let mut trait_ = extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?.clone();
216
217 let trait_name_ident = &trait_
220 .segments
221 .last()
222 .as_ref()
223 .expect("Trait path should always contain at least one item; qed")
224 .ident;
225
226 let generics = trait_
229 .segments
230 .iter()
231 .find_map(|segment| {
232 if let syn::PathArguments::AngleBracketed(generics) = &segment.arguments {
233 Some(generics.clone())
234 } else {
235 None
236 }
237 })
238 .expect("Trait path should always contain at least one generic parameter; qed");
239
240 let mod_name = generate_runtime_mod_name_for_trait(&trait_name_ident);
241 if let Some(segment) = trait_.segments.last_mut() {
243 *segment = parse_quote!(#mod_name);
244 }
245
246 let runtime_metadata_call = {
248 let api_version = extract_api_version(&impl_.attrs, impl_.span())?;
249
250 let base_version = if let Some(version) = api_version.custom {
254 quote! { #version }
255 } else {
256 quote! { #trait_::VERSION }
257 };
258
259 if let Some(cfg_version) = api_version.feature_gated {
263 let cfg_feature = cfg_version.0;
264 let cfg_version = cfg_version.1;
265 quote! {{
266 if cfg!(feature = #cfg_feature) {
267 #trait_::runtime_metadata::#generics(#cfg_version)
268 } else {
269 #trait_::runtime_metadata::#generics(#base_version)
270 }
271 }}
272 } else {
273 quote! {
274 #trait_::runtime_metadata::#generics(#base_version)
275 }
276 }
277 };
278
279 let attrs = filter_cfg_attributes(&impl_.attrs);
280 metadata.push(quote!(
281 #( #attrs )*
282 #runtime_metadata_call
283 ));
284 }
285
286 Ok(quote!(
303 #crate_::frame_metadata_enabled! {
304 #[doc(hidden)]
305 impl #crate_::metadata_ir::InternalImplRuntimeApis for #runtime_name {
306 fn runtime_metadata(&self) -> #crate_::vec::Vec<#crate_::metadata_ir::RuntimeApiMetadataIR> {
307 #crate_::vec![ #( #metadata, )* ]
308 }
309 }
310 }
311 ))
312}