ink_codegen/generator/sol/
metadata.rs1use derive_more::From;
16use ir::{
17 Callable as _,
18 InputsIter,
19};
20use proc_macro2::TokenStream as TokenStream2;
21use quote::quote;
22use syn::Pat;
23
24use super::utils::{
25 extract_docs,
26 sol_return_type,
27 sol_type,
28};
29use crate::GenerateCode;
30
31#[derive(From)]
33pub struct SolidityMetadata<'a> {
34 contract: &'a ir::Contract,
36}
37impl_as_ref_for_generator!(SolidityMetadata);
38
39impl GenerateCode for SolidityMetadata<'_> {
40 fn generate_code(&self) -> TokenStream2 {
41 let ident = self.contract.module().storage().ident();
42 let name = ident.to_string();
43 let ctors = self.constructors();
44 let msgs = self.messages();
45 let docs = extract_docs(self.contract.module().attrs());
46
47 quote! {
48 #[cfg(feature = "std")]
49 #[cfg(not(feature = "ink-as-dependency"))]
50 #[cfg(any(ink_abi = "sol", ink_abi = "all"))]
51 const _: () = {
52 #[unsafe(no_mangle)]
53 pub fn __ink_generate_solidity_metadata() -> ::ink::metadata::sol::ContractMetadata {
54 ::ink::metadata::sol::ContractMetadata {
55 name: #name.into(),
56 constructors: vec![ #( #ctors ),* ],
57 functions: vec![ #( #msgs ),* ],
58 events: ::ink::collect_events_sol(),
59 errors: ::ink::collect_errors_sol(),
60 docs: #docs.into(),
61 }
62 }
63 };
64 }
65 }
66}
67
68impl SolidityMetadata<'_> {
69 fn constructors(&self) -> impl Iterator<Item = TokenStream2> + '_ {
71 self.contract
72 .module()
73 .impls()
74 .flat_map(|item_impl| item_impl.iter_constructors())
75 .map(|ctor| {
76 let name = ctor
77 .name()
78 .map(ToString::to_string)
79 .unwrap_or_else(|| ctor.ident().to_string());
80 let inputs = params_info(ctor.inputs());
81 let is_payable = ctor.is_payable();
82 let is_default = ctor.is_default();
83 let docs = extract_docs(ctor.attrs());
84
85 quote! {
86 ::ink::metadata::sol::ConstructorMetadata {
87 name: #name.into(),
88 inputs: vec![ #( #inputs ),* ],
89 is_payable: #is_payable,
90 is_default: #is_default,
91 docs: #docs.into(),
92 }
93 }
94 })
95 }
96
97 fn messages(&self) -> impl Iterator<Item = TokenStream2> + '_ {
99 self.contract
100 .module()
101 .impls()
102 .flat_map(|item_impl| item_impl.iter_messages())
103 .map(|msg| {
104 let name = msg
105 .name()
106 .map(ToString::to_string)
107 .unwrap_or_else(|| msg.ident().to_string());
108 let inputs = params_info(msg.inputs());
109 let output = msg
110 .output()
111 .map(|ty| {
112 let sol_ty = sol_return_type(ty);
113 quote! { ::core::option::Option::Some(#sol_ty.into()) }
114 })
115 .unwrap_or_else(|| {
116 quote! { ::core::option::Option::None }
117 });
118 let mutates = msg.receiver().is_ref_mut();
119 let is_payable = msg.is_payable();
120 let is_default = msg.is_default();
121 let docs = extract_docs(msg.attrs());
122
123 quote! {
124 ::ink::metadata::sol::FunctionMetadata {
125 name: #name.into(),
126 inputs: vec![ #( #inputs ),* ],
127 output: #output,
128 mutates: #mutates,
129 is_payable: #is_payable,
130 is_default: #is_default,
131 docs: #docs.into(),
132 }
133 }
134 })
135 }
136}
137
138fn params_info(inputs: InputsIter<'_>) -> impl Iterator<Item = TokenStream2> + '_ {
140 inputs.map(|input| {
141 let ty = &*input.ty;
142 let sol_ty = sol_type(ty);
143 let ident = match &*input.pat {
144 Pat::Ident(ident) => &ident.ident,
145 _ => unreachable!("Expected an input identifier"),
146 };
147 let name = ident.to_string();
148 quote! {
149 ::ink::metadata::sol::ParamMetadata {
150 name: #name.into(),
151 ty: #sol_ty.into(),
152 }
153 }
154 })
155}