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