1212// See the License for the specific language governing permissions and
1313// limitations under the License.
1414
15- use proc_macro2:: TokenStream as TokenStream2 ;
15+ use ink_ir:: IsDocAttribute ;
16+ use proc_macro2:: {
17+ Ident ,
18+ TokenStream as TokenStream2 ,
19+ } ;
1620use quote:: {
1721 format_ident,
1822 quote,
1923} ;
2024use syn:: {
2125 spanned:: Spanned ,
26+ Attribute ,
27+ Field ,
2228 Fields ,
2329} ;
2430
@@ -62,6 +68,27 @@ pub fn sol_error_encode_derive(s: synstructure::Structure) -> TokenStream2 {
6268 }
6369}
6470
71+ /// Derives the `ink::metadata::sol::SolErrorMetadata` trait for the given `struct` or
72+ /// `enum`.
73+ pub fn sol_error_metadata_derive ( s : synstructure:: Structure ) -> TokenStream2 {
74+ match s. ast ( ) . data {
75+ syn:: Data :: Struct ( _) => {
76+ sol_error_metadata_derive_struct ( s)
77+ . unwrap_or_else ( |err| err. to_compile_error ( ) )
78+ }
79+ syn:: Data :: Enum ( _) => {
80+ sol_error_metadata_derive_enum ( s) . unwrap_or_else ( |err| err. to_compile_error ( ) )
81+ }
82+ _ => {
83+ syn:: Error :: new (
84+ s. ast ( ) . span ( ) ,
85+ "can only derive `SolErrorEncode` for Rust `struct` and `enum` items" ,
86+ )
87+ . to_compile_error ( )
88+ }
89+ }
90+ }
91+
6592/// Derives the `ink::sol::SolErrorDecode` trait for the given `struct`.
6693fn sol_error_decode_derive_struct (
6794 s : synstructure:: Structure ,
@@ -149,6 +176,43 @@ fn sol_error_encode_derive_struct(
149176 ) )
150177}
151178
179+ /// Derives the `ink::metadata::sol::SolErrorMetadata` trait for the given `struct`.
180+ fn sol_error_metadata_derive_struct (
181+ s : synstructure:: Structure ,
182+ ) -> syn:: Result < TokenStream2 > {
183+ ensure_no_generics ( & s, "SolErrorMetadata" ) ?;
184+
185+ let Some ( variant) = s. variants ( ) . first ( ) else {
186+ return Err ( syn:: Error :: new (
187+ s. ast ( ) . span ( ) ,
188+ "can only derive `SolErrorMetadata` for Rust `struct` items" ,
189+ ) ) ;
190+ } ;
191+
192+ let ident = & s. ast ( ) . ident ;
193+ let name = ident. to_string ( ) ;
194+ let params = variant. ast ( ) . fields . iter ( ) . map ( param_metadata_from_field) ;
195+ let docs = extract_docs ( s. ast ( ) . attrs . as_slice ( ) ) ;
196+ let metadata_linker = register_metadata ( ident) ;
197+
198+ Ok ( s. bound_impl (
199+ quote ! ( :: ink:: metadata:: sol:: SolErrorMetadata ) ,
200+ quote ! {
201+ fn error_specs( ) -> :: ink:: prelude:: vec:: Vec <:: ink:: metadata:: sol:: ErrorMetadata > {
202+ #metadata_linker
203+
204+ vec![
205+ :: ink:: metadata:: sol:: ErrorMetadata {
206+ name: #name. into( ) ,
207+ params: vec![ #( #params ) , * ] ,
208+ docs: #docs. into( ) ,
209+ }
210+ ]
211+ }
212+ } ,
213+ ) )
214+ }
215+
152216/// Derives the `ink::sol::SolErrorDecode` trait for the given `enum`.
153217fn sol_error_decode_derive_enum ( s : synstructure:: Structure ) -> syn:: Result < TokenStream2 > {
154218 ensure_no_generics ( & s, "SolErrorDecode" ) ?;
@@ -297,6 +361,41 @@ fn sol_error_encode_derive_enum(s: synstructure::Structure) -> syn::Result<Token
297361 ) )
298362}
299363
364+ /// Derives the `ink::metadata::sol::SolErrorMetadata` trait for the given `enum`.
365+ fn sol_error_metadata_derive_enum (
366+ s : synstructure:: Structure ,
367+ ) -> syn:: Result < TokenStream2 > {
368+ ensure_no_generics ( & s, "SolErrorMetadata" ) ?;
369+ utils:: ensure_non_empty_enum ( & s, "SolErrorMetadata" ) ?;
370+
371+ let error_variants = s. variants ( ) . iter ( ) . map ( |variant| {
372+ let variant_ident = variant. ast ( ) . ident ;
373+ let variant_name = variant_ident. to_string ( ) ;
374+ let params = variant. ast ( ) . fields . iter ( ) . map ( param_metadata_from_field) ;
375+ let docs = extract_docs ( variant. ast ( ) . attrs ) ;
376+
377+ quote ! {
378+ :: ink:: metadata:: sol:: ErrorMetadata {
379+ name: #variant_name. into( ) ,
380+ params: vec![ #( #params ) , * ] ,
381+ docs: #docs. into( ) ,
382+ }
383+ }
384+ } ) ;
385+ let metadata_linker = register_metadata ( & s. ast ( ) . ident ) ;
386+
387+ Ok ( s. bound_impl (
388+ quote ! ( :: ink:: metadata:: sol:: SolErrorMetadata ) ,
389+ quote ! {
390+ fn error_specs( ) -> :: ink:: prelude:: vec:: Vec <:: ink:: metadata:: sol:: ErrorMetadata > {
391+ #metadata_linker
392+
393+ vec![ #( #error_variants ) , * ]
394+ }
395+ } ,
396+ ) )
397+ }
398+
300399/// Ensures that the given item has no generics.
301400fn ensure_no_generics ( s : & synstructure:: Structure , trait_name : & str ) -> syn:: Result < ( ) > {
302401 if s. ast ( ) . generics . params . is_empty ( ) {
@@ -311,3 +410,44 @@ fn ensure_no_generics(s: &synstructure::Structure, trait_name: &str) -> syn::Res
311410 ) )
312411 }
313412}
413+
414+ /// Register an error metadata function in the distributed slice for combining all
415+ /// errors referenced in the contract binary.
416+ fn register_metadata ( ident : & Ident ) -> TokenStream2 {
417+ quote ! {
418+ #[ :: ink:: linkme:: distributed_slice( :: ink:: CONTRACT_ERRORS_SOL ) ]
419+ #[ linkme( crate = :: ink:: linkme) ]
420+ static ERROR_METADATA : fn ( ) -> :: ink:: prelude:: vec:: Vec <:: ink:: metadata:: sol:: ErrorMetadata > =
421+ <#ident as :: ink:: metadata:: sol:: SolErrorMetadata >:: error_specs;
422+ }
423+ }
424+
425+ /// Returns the error parameter from the given field.
426+ fn param_metadata_from_field ( field : & Field ) -> TokenStream2 {
427+ let ty = & field. ty ;
428+ let name = field
429+ . ident
430+ . as_ref ( )
431+ . map ( ToString :: to_string)
432+ . unwrap_or_default ( ) ;
433+ let docs = extract_docs ( field. attrs . as_slice ( ) ) ;
434+ let sol_ty = quote ! {
435+ <#ty as :: ink:: SolEncode >:: SOL_NAME
436+ } ;
437+ quote ! {
438+ :: ink:: metadata:: sol:: ErrorParamMetadata {
439+ name: #name. into( ) ,
440+ ty: #sol_ty. into( ) ,
441+ docs: #docs. into( ) ,
442+ }
443+ }
444+ }
445+
446+ /// Returns the rustdoc string from the given item attributes.
447+ fn extract_docs ( attrs : & [ Attribute ] ) -> String {
448+ attrs
449+ . iter ( )
450+ . filter_map ( |attr| attr. extract_docs ( ) )
451+ . collect :: < Vec < _ > > ( )
452+ . join ( "\n " )
453+ }
0 commit comments