alloy_sol_macro_expander/expand/
to_abi.rs1use super::ExpCtxt;
2use crate::verbatim::Verbatim;
3use alloy_json_abi::{
4 Constructor, Error, Event, EventParam, Fallback, Function, Param, Receive, StateMutability,
5};
6use ast::{ItemError, ItemEvent, ItemFunction};
7use proc_macro2::TokenStream;
8use quote::quote;
9use std::fmt::Write;
10
11pub(crate) fn generate<T>(t: &T, cx: &ExpCtxt<'_>) -> TokenStream
12where
13 T: ToAbi,
14 T::DynAbi: Verbatim,
15{
16 crate::verbatim::verbatim(&t.to_dyn_abi(cx), &cx.crates)
17}
18
19pub(crate) trait ToAbi {
20 type DynAbi;
21
22 fn to_dyn_abi(&self, cx: &ExpCtxt<'_>) -> Self::DynAbi;
23}
24
25impl ToAbi for ast::ItemFunction {
26 type DynAbi = Function;
27
28 fn to_dyn_abi(&self, cx: &ExpCtxt<'_>) -> Self::DynAbi {
29 Function {
30 name: self.name.as_ref().map(|i| i.as_string()).unwrap_or_default(),
31 inputs: self.parameters.to_dyn_abi(cx),
32 outputs: self.returns.as_ref().map(|r| r.returns.to_dyn_abi(cx)).unwrap_or_default(),
33 state_mutability: self.attributes.to_dyn_abi(cx),
34 }
35 }
36}
37
38impl ToAbi for ast::ItemError {
39 type DynAbi = Error;
40
41 fn to_dyn_abi(&self, cx: &ExpCtxt<'_>) -> Self::DynAbi {
42 Error { name: self.name.as_string(), inputs: self.parameters.to_dyn_abi(cx) }
43 }
44}
45
46impl ToAbi for ast::ItemEvent {
47 type DynAbi = Event;
48
49 fn to_dyn_abi(&self, cx: &ExpCtxt<'_>) -> Self::DynAbi {
50 Event {
51 name: self.name.as_string(),
52 inputs: self.parameters.iter().map(|e| e.to_dyn_abi(cx)).collect(),
53 anonymous: self.is_anonymous(),
54 }
55 }
56}
57
58impl<P> ToAbi for ast::Parameters<P> {
59 type DynAbi = Vec<Param>;
60
61 fn to_dyn_abi(&self, cx: &ExpCtxt<'_>) -> Self::DynAbi {
62 self.iter().map(|p| p.to_dyn_abi(cx)).collect()
63 }
64}
65
66impl ToAbi for ast::VariableDeclaration {
67 type DynAbi = Param;
68
69 fn to_dyn_abi(&self, cx: &ExpCtxt<'_>) -> Self::DynAbi {
70 ty_to_param(self.name.as_ref().map(ast::SolIdent::as_string), &self.ty, cx)
71 }
72}
73
74impl ToAbi for ast::EventParameter {
75 type DynAbi = EventParam;
76
77 fn to_dyn_abi(&self, cx: &ExpCtxt<'_>) -> Self::DynAbi {
78 let name = self.name.as_ref().map(ast::SolIdent::as_string);
79 let Param { ty, name, components, internal_type } = ty_to_param(name, &self.ty, cx);
80 EventParam { ty, name, indexed: self.is_indexed(), internal_type, components }
81 }
82}
83
84impl ToAbi for ast::FunctionAttributes {
85 type DynAbi = StateMutability;
86
87 fn to_dyn_abi(&self, _cx: &ExpCtxt<'_>) -> Self::DynAbi {
88 match self.mutability() {
89 Some(ast::Mutability::Pure(_) | ast::Mutability::Constant(_)) => StateMutability::Pure,
90 Some(ast::Mutability::View(_)) => StateMutability::View,
91 Some(ast::Mutability::Payable(_)) => StateMutability::Payable,
92 None => StateMutability::NonPayable,
93 }
94 }
95}
96
97fn ty_to_param(name: Option<String>, ty: &ast::Type, cx: &ExpCtxt<'_>) -> Param {
98 let mut ty_name = ty_abi_string(ty, cx);
99
100 if ty_name.starts_with('(') {
103 let paren_i = ty_name.rfind(')').expect("malformed tuple type");
104 let suffix = &ty_name[paren_i + 1..];
105 ty_name = format!("tuple{suffix}");
106 }
107
108 let resolved = match ty.peel_arrays() {
109 ast::Type::Custom(custom_name) => {
110 if let ast::Item::Struct(s) = cx.item(custom_name) {
111 return Param {
112 ty: ty_name,
113 name: name.unwrap_or_default(),
114 internal_type: None,
115 components: s.fields.to_dyn_abi(cx),
116 };
117 }
118 cx.custom_type(custom_name)
119 }
120 ty => ty,
121 };
122
123 let components = if let ast::Type::Tuple(tuple) = resolved {
124 tuple.types.iter().map(|ty| ty_to_param(None, ty, cx)).collect()
125 } else {
126 vec![]
127 };
128
129 let internal_type = None;
131
132 Param { ty: ty_name, name: name.unwrap_or_default(), internal_type, components }
133}
134
135fn ty_abi_string(ty: &ast::Type, cx: &ExpCtxt<'_>) -> String {
136 let mut suffix = String::new();
137 rec_ty_abi_string_suffix(cx, ty, &mut suffix);
138
139 let mut ty = ty.peel_arrays();
140 if let ast::Type::Custom(name) = ty {
141 match cx.try_custom_type(name) {
142 Some(ast::Type::Tuple(_)) => return format!("tuple{suffix}"),
143 Some(custom) => ty = custom,
144 None => {}
145 }
146 }
147 format!("{}{suffix}", super::ty::TypePrinter::new(cx, ty))
148}
149
150fn rec_ty_abi_string_suffix(cx: &ExpCtxt<'_>, ty: &ast::Type, s: &mut String) {
151 if let ast::Type::Array(array) = ty {
152 rec_ty_abi_string_suffix(cx, &array.ty, s);
153 if let Some(size) = cx.eval_array_size(array) {
154 write!(s, "[{size}]").unwrap();
155 } else {
156 s.push_str("[]");
157 }
158 }
159}
160
161pub(super) fn constructor(function: &ItemFunction, cx: &ExpCtxt<'_>) -> Constructor {
162 assert!(function.kind.is_constructor());
163 Constructor {
164 inputs: function.parameters.to_dyn_abi(cx),
165 state_mutability: function.attributes.to_dyn_abi(cx),
166 }
167}
168
169pub(super) fn fallback(function: &ItemFunction, cx: &ExpCtxt<'_>) -> Fallback {
170 assert!(function.kind.is_fallback());
171 Fallback { state_mutability: function.attributes.to_dyn_abi(cx) }
172}
173
174pub(super) fn receive(function: &ItemFunction, _cx: &ExpCtxt<'_>) -> Receive {
175 assert!(function.kind.is_receive());
176 Receive { state_mutability: StateMutability::Payable }
177}
178
179macro_rules! make_map {
180 ($items:expr, $cx:expr, $get_ident:ident, $ty:ident) => {{
181 let mut items_map = std::collections::BTreeMap::<String, Vec<_>>::new();
182 let alloy_sol_types = &$cx.crates.sol_types;
183 for item in $items {
184 let name = item.to_dyn_abi($cx).name;
185 let ident = $cx.$get_ident(item.into());
186 let item = quote::quote!(<#ident as #alloy_sol_types::JsonAbiExt>::abi);
187 items_map.entry(name).or_default().push(item);
188 }
189 let items = items_map.into_iter().map(|(name, items)| {
190 quote!((#name, &[#(#items),*]))
191 });
192 quote! {
193 static LAZY_ITEMS: &[(&str, &[fn() -> #alloy_sol_types::private::alloy_json_abi::$ty])] = &[#(#items,)*];
194 let items = LAZY_ITEMS
195 .iter()
196 .map(|(name, item_fns)| (
197 alloy_sol_types::private::str_to_owned(name),
198 item_fns.iter().map(|item_fn| item_fn()).collect()
199 ))
200 .collect();
201 alloy_sol_types::private::make_btree_map(items)
202 }
203 }};
204}
205
206pub(super) fn functions_map(functions: &[ItemFunction], cx: &ExpCtxt<'_>) -> TokenStream {
207 make_map!(functions, cx, call_name, Function)
208}
209
210pub(super) fn events_map(events: &[&ItemEvent], cx: &ExpCtxt<'_>) -> TokenStream {
211 make_map!(events.iter().copied(), cx, overloaded_name, Event)
212}
213
214pub(super) fn errors_map(errors: &[&ItemError], cx: &ExpCtxt<'_>) -> TokenStream {
215 make_map!(errors.iter().copied(), cx, overloaded_name, Error)
216}