1#![doc = include_str!("../README.md")]
2#![doc(
3 html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg",
4 html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico"
5)]
6#![cfg_attr(not(test), warn(unused_crate_dependencies))]
7#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
8#![allow(clippy::option_if_let_else)]
9
10mod expand;
11mod parse;
12mod serde;
13
14use expand::Expander;
15use parse::{EnvelopeArgs, GroupedVariants};
16use proc_macro::TokenStream;
17use quote::quote;
18use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Ident};
19
20#[proc_macro_derive(TransactionEnvelope, attributes(envelope, serde))]
43pub fn derive_transaction_envelope(input: TokenStream) -> TokenStream {
44 let input = parse_macro_input!(input as DeriveInput);
45
46 match expand_transaction_envelope(input) {
47 Ok(tokens) => tokens.into(),
48 Err(err) => err.to_compile_error().into(),
49 }
50}
51
52fn expand_transaction_envelope(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
54 use darling::FromDeriveInput;
55
56 let args = EnvelopeArgs::from_derive_input(&input)
58 .map_err(|e| Error::new_spanned(&input.ident, e.to_string()))?;
59
60 let input_type_name = args.ident.clone();
62 let tx_type_enum_name = args
63 .tx_type_name
64 .clone()
65 .unwrap_or_else(|| Ident::new(&format!("{input_type_name}Type"), input_type_name.span()));
66 let alloy_consensus =
67 args.alloy_consensus.clone().unwrap_or_else(|| parse_quote!(::alloy_consensus));
68 let generics = args.generics.clone();
69 let serde_cfg = match args.serde_cfg.as_ref() {
70 Some(syn::Meta::List(list)) => list.tokens.clone(),
71 Some(_) => {
72 return Err(Error::new_spanned(
73 &input.ident,
74 "serde_cfg must be a list like `serde_cfg(feature = \"serde\")`",
75 ))
76 }
77 None => quote! { all() },
79 };
80
81 let arbitrary_cfg = match args.arbitrary_cfg.as_ref() {
82 Some(syn::Meta::List(list)) => list.tokens.clone(),
83 Some(_) => {
84 return Err(Error::new_spanned(
85 &input.ident,
86 "arbitrary_cfg must be a list like `arbitrary_cfg(feature = \"arbitrary\")`",
87 ))
88 }
89 None => quote! { all() },
90 };
91
92 let variants = GroupedVariants::from_args(args)?;
93
94 let alloy_primitives = quote! { #alloy_consensus::private::alloy_primitives };
95 let alloy_eips = quote! { #alloy_consensus::private::alloy_eips };
96 let alloy_rlp = quote! { #alloy_consensus::private::alloy_rlp };
97
98 let expander = Expander {
100 input_type_name,
101 tx_type_enum_name,
102 alloy_consensus,
103 generics,
104 serde_enabled: cfg!(feature = "serde"),
105 serde_cfg,
106 arbitrary_cfg,
107 arbitrary_enabled: cfg!(feature = "arbitrary"),
108 alloy_primitives,
109 alloy_eips,
110 alloy_rlp,
111 variants,
112 };
113 Ok(expander.expand())
114}