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))]
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))]
48pub fn derive_transaction_envelope(input: TokenStream) -> TokenStream {
49 let input = parse_macro_input!(input as DeriveInput);
50
51 match expand_transaction_envelope(input) {
52 Ok(tokens) => tokens.into(),
53 Err(err) => err.to_compile_error().into(),
54 }
55}
56
57fn expand_transaction_envelope(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
59 use darling::FromDeriveInput;
60
61 let args = EnvelopeArgs::from_derive_input(&input)
63 .map_err(|e| Error::new_spanned(&input.ident, e.to_string()))?;
64
65 let input_type_name = args.ident.clone();
67 let tx_type_enum_name = args
68 .tx_type_name
69 .clone()
70 .unwrap_or_else(|| Ident::new(&format!("{input_type_name}Type"), input_type_name.span()));
71 let alloy_consensus =
72 args.alloy_consensus.clone().unwrap_or_else(|| parse_quote!(::alloy_consensus));
73 let generics = args.generics.clone();
74 let typed = args.typed.clone();
75 let serde_cfg = match args.serde_cfg.as_ref() {
76 Some(syn::Meta::List(list)) => list.tokens.clone(),
77 Some(_) => {
78 return Err(Error::new_spanned(
79 &input.ident,
80 "serde_cfg must be a list like `serde_cfg(feature = \"serde\")`",
81 ))
82 }
83 None => quote! { all() },
85 };
86
87 let arbitrary_cfg = match args.arbitrary_cfg.as_ref() {
88 Some(syn::Meta::List(list)) => list.tokens.clone(),
89 Some(_) => {
90 return Err(Error::new_spanned(
91 &input.ident,
92 "arbitrary_cfg must be a list like `arbitrary_cfg(feature = \"arbitrary\")`",
93 ))
94 }
95 None => quote! { all() },
96 };
97
98 let variants = GroupedVariants::from_args(args)?;
99
100 let alloy_primitives = quote! { #alloy_consensus::private::alloy_primitives };
101 let alloy_eips = quote! { #alloy_consensus::private::alloy_eips };
102 let alloy_rlp = quote! { #alloy_consensus::private::alloy_rlp };
103
104 let expander = Expander {
106 input_type_name,
107 tx_type_enum_name,
108 alloy_consensus,
109 generics,
110 serde_enabled: cfg!(feature = "serde"),
111 serde_cfg,
112 arbitrary_cfg,
113 arbitrary_enabled: cfg!(feature = "arbitrary"),
114 alloy_primitives,
115 alloy_eips,
116 alloy_rlp,
117 variants,
118 typed,
119 };
120 Ok(expander.expand())
121}