sp_runtime_interface_proc_macro/runtime_interface/
trait_decl_impl.rs1use crate::utils::{
22 create_function_ident_with_version, generate_crate_access,
23 get_function_argument_types_without_ref, get_runtime_interface,
24};
25
26use syn::{
27 fold::{self, Fold},
28 spanned::Spanned,
29 Error, Generics, ItemTrait, Receiver, Result, TraitItemFn, Type, Visibility,
30};
31
32use proc_macro2::TokenStream;
33
34use quote::quote;
35
36pub fn process(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
39 let impl_trait = impl_trait_for_externalities(trait_def, is_wasm_only)?;
40 let essential_trait_def = declare_essential_trait(trait_def)?;
41
42 Ok(quote! {
43 #impl_trait
44
45 #essential_trait_def
46 })
47}
48
49struct ToEssentialTraitDef {
52 errors: Vec<Error>,
54 methods: Vec<TraitItemFn>,
55}
56
57impl ToEssentialTraitDef {
58 fn new() -> Self {
59 ToEssentialTraitDef { errors: vec![], methods: vec![] }
60 }
61
62 fn into_methods(self) -> Result<Vec<TraitItemFn>> {
63 let mut errors = self.errors;
64 let methods = self.methods;
65 if let Some(first_error) = errors.pop() {
66 Err(errors.into_iter().fold(first_error, |mut o, n| {
67 o.combine(n);
68 o
69 }))
70 } else {
71 Ok(methods)
72 }
73 }
74
75 fn process(&mut self, method: &TraitItemFn, version: u32) {
76 let mut folded = self.fold_trait_item_fn(method.clone());
77 folded.sig.ident = create_function_ident_with_version(&folded.sig.ident, version);
78 self.methods.push(folded);
79 }
80
81 fn push_error<S: Spanned>(&mut self, span: &S, msg: &str) {
82 self.errors.push(Error::new(span.span(), msg));
83 }
84
85 fn error_on_generic_parameters(&mut self, generics: &Generics) {
86 if let Some(param) = generics.params.first() {
87 self.push_error(param, "Generic parameters not supported.");
88 }
89 }
90}
91
92impl Fold for ToEssentialTraitDef {
93 fn fold_trait_item_fn(&mut self, mut method: TraitItemFn) -> TraitItemFn {
94 if method.default.take().is_none() {
95 self.push_error(&method, "Methods need to have an implementation.");
96 }
97
98 let arg_types = get_function_argument_types_without_ref(&method.sig);
99 arg_types
100 .filter_map(|ty| match *ty {
101 Type::ImplTrait(impl_trait) => Some(impl_trait),
102 _ => None,
103 })
104 .for_each(|invalid| self.push_error(&invalid, "`impl Trait` syntax not supported."));
105
106 self.error_on_generic_parameters(&method.sig.generics);
107
108 method.attrs.retain(|a| !a.path().is_ident("version"));
109
110 fold::fold_trait_item_fn(self, method)
111 }
112
113 fn fold_item_trait(&mut self, mut trait_def: ItemTrait) -> ItemTrait {
114 self.error_on_generic_parameters(&trait_def.generics);
115
116 trait_def.vis = Visibility::Inherited;
117 fold::fold_item_trait(self, trait_def)
118 }
119
120 fn fold_receiver(&mut self, receiver: Receiver) -> Receiver {
121 if receiver.reference.is_none() {
122 self.push_error(&receiver, "Taking `Self` by value is not allowed.");
123 }
124
125 fold::fold_receiver(self, receiver)
126 }
127}
128
129fn declare_essential_trait(trait_def: &ItemTrait) -> Result<TokenStream> {
130 let trait_ = &trait_def.ident;
131
132 if let Some(param) = trait_def.generics.params.first() {
133 return Err(Error::new(param.span(), "Generic parameters not supported."))
134 }
135
136 let interface = get_runtime_interface(trait_def)?;
137 let mut folder = ToEssentialTraitDef::new();
138 for (version, interface_method) in interface.all_versions() {
139 folder.process(interface_method, version);
140 }
141 let methods = folder.into_methods()?;
142
143 Ok(quote! {
144 trait #trait_ {
145 #( #methods )*
146 }
147 })
148}
149
150fn impl_trait_for_externalities(trait_def: &ItemTrait, is_wasm_only: bool) -> Result<TokenStream> {
152 let trait_ = &trait_def.ident;
153 let crate_ = generate_crate_access();
154 let interface = get_runtime_interface(trait_def)?;
155 let methods = interface.all_versions().map(|(version, method)| {
156 let mut cloned = (*method).clone();
157 cloned.attrs.retain(|a| !a.path().is_ident("version"));
158 cloned.sig.ident = create_function_ident_with_version(&cloned.sig.ident, version);
159 cloned
160 });
161
162 let impl_type = if is_wasm_only {
163 quote!( &mut dyn #crate_::sp_wasm_interface::FunctionContext )
164 } else {
165 quote!( &mut dyn #crate_::Externalities )
166 };
167
168 Ok(quote! {
169 #[cfg(feature = "std")]
170 impl #trait_ for #impl_type {
171 #( #methods )*
172 }
173 })
174}