1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! [`ItemUdt`] expansion.

use super::{ty::expand_rust_type, ExpCtxt};
use crate::expand::expand_type;
use alloy_sol_macro_input::ContainsSolAttrs;
use ast::ItemUdt;
use proc_macro2::TokenStream;
use quote::quote;
use syn::Result;

pub(super) fn expand(cx: &ExpCtxt<'_>, udt: &ItemUdt) -> Result<TokenStream> {
    let ItemUdt { name, ty, .. } = udt;

    let (sol_attrs, mut attrs) = udt.split_attrs()?;
    cx.type_derives(&mut attrs, std::iter::once(ty), true);

    let underlying_sol = expand_type(ty, &cx.crates);
    let underlying_rust = expand_rust_type(ty, &cx.crates);

    let type_check_body = if let Some(lit_str) = sol_attrs.type_check {
        let func_path: syn::Path = lit_str.parse()?;
        quote! {
            <#underlying_sol as alloy_sol_types::SolType>::type_check(token)?;
            #func_path(token)
        }
    } else {
        quote! {
            <#underlying_sol as alloy_sol_types::SolType>::type_check(token)
        }
    };

    let alloy_sol_types = &cx.crates.sol_types;

    let tokens = quote! {
        #(#attrs)*
        #[allow(non_camel_case_types, non_snake_case)]
        #[derive(Clone)]
        pub struct #name(#underlying_rust);

        const _: () = {
            use #alloy_sol_types as alloy_sol_types;

            #[automatically_derived]
            impl alloy_sol_types::private::SolTypeValue<#name> for #underlying_rust {
                #[inline]
                fn stv_to_tokens(&self) -> <#underlying_sol as alloy_sol_types::SolType>::Token<'_> {
                    alloy_sol_types::private::SolTypeValue::<#underlying_sol>::stv_to_tokens(self)
                }

                #[inline]
                fn stv_eip712_data_word(&self) -> alloy_sol_types::Word {
                    <#underlying_sol as alloy_sol_types::SolType>::tokenize(self).0
                }

                #[inline]
                fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec<u8>) {
                    <#underlying_sol as alloy_sol_types::SolType>::abi_encode_packed_to(self, out)
                }

                #[inline]
                fn stv_abi_packed_encoded_size(&self) -> usize {
                    <#underlying_sol as alloy_sol_types::SolType>::abi_encoded_size(self)
                }
            }

            #[automatically_derived]
            impl #name {
                /// The Solidity type name.
                pub const NAME: &'static str = stringify!(@name);

                /// Convert from the underlying value type.
                #[inline]
                pub const fn from(value: #underlying_rust) -> Self {
                    Self(value)
                }

                /// Return the underlying value.
                #[inline]
                pub const fn into(self) -> #underlying_rust {
                    self.0
                }

                /// Return the single encoding of this value, delegating to the
                /// underlying type.
                #[inline]
                pub fn abi_encode(&self) -> alloy_sol_types::private::Vec<u8> {
                    <Self as alloy_sol_types::SolType>::abi_encode(&self.0)
                }

                /// Return the packed encoding of this value, delegating to the
                /// underlying type.
                #[inline]
                pub fn abi_encode_packed(&self) -> alloy_sol_types::private::Vec<u8> {
                    <Self as alloy_sol_types::SolType>::abi_encode_packed(&self.0)
                }
            }

            #[automatically_derived]
            impl alloy_sol_types::SolType for #name {
                type RustType = #underlying_rust;
                type Token<'a> = <#underlying_sol as alloy_sol_types::SolType>::Token<'a>;

                const SOL_NAME: &'static str = Self::NAME;
                const ENCODED_SIZE: Option<usize> = <#underlying_sol as alloy_sol_types::SolType>::ENCODED_SIZE;
                const PACKED_ENCODED_SIZE: Option<usize> = <#underlying_sol as alloy_sol_types::SolType>::PACKED_ENCODED_SIZE;

                #[inline]
                fn valid_token(token: &Self::Token<'_>) -> bool {
                    Self::type_check(token).is_ok()
                }

                #[inline]
                fn type_check(token: &Self::Token<'_>) -> alloy_sol_types::Result<()> {
                    #type_check_body
                }

                #[inline]
                fn detokenize(token: Self::Token<'_>) -> Self::RustType {
                    <#underlying_sol as alloy_sol_types::SolType>::detokenize(token)
                }
            }

            #[automatically_derived]
            impl alloy_sol_types::EventTopic for #name {
                #[inline]
                fn topic_preimage_length(rust: &Self::RustType) -> usize {
                    <#underlying_sol as alloy_sol_types::EventTopic>::topic_preimage_length(rust)
                }

                #[inline]
                fn encode_topic_preimage(rust: &Self::RustType, out: &mut alloy_sol_types::private::Vec<u8>) {
                    <#underlying_sol as alloy_sol_types::EventTopic>::encode_topic_preimage(rust, out)
                }

                #[inline]
                fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken {
                    <#underlying_sol as alloy_sol_types::EventTopic>::encode_topic(rust)
                }
            }
        };
    };
    Ok(tokens)
}