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    // HACK: `cx.custom_type` resolves the custom type recursively, so in recursive structs the
100    // peeled `ty` will be `Tuple` rather than `Custom`.
101    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    // TODO: internal_type
134    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}