cido_ethereum_macros/
lib.rs1use proc_macro2::{Span, TokenStream};
2use std::path::Path;
3
4mod cido_meta;
5mod contract;
6pub(crate) mod types;
7mod util;
8
9#[proc_macro_attribute]
11pub fn ethereum_contract(
12 args: proc_macro::TokenStream,
13 input: proc_macro::TokenStream,
14) -> proc_macro::TokenStream {
15 crate::contract::RawContract::from_input(args.into(), input.into())
16 .and_then(|rc| rc.derived())
17 .unwrap_or_else(|e| e.into_compile_error())
18 .into()
19}
20
21pub(crate) fn err(span: &dyn syn::spanned::Spanned, t: impl core::fmt::Display) -> syn::Error {
23 syn::Error::new(span.span(), t)
24}
25
26pub(crate) fn ensure_generics_zero(generics: &syn::Generics) -> deluxe::Result<()> {
27 if generics.const_params().next().is_some()
28 || generics.lifetimes().next().is_some()
29 || generics.type_params().next().is_some()
30 {
31 return Err(deluxe::Error::new(
32 Span::call_site(),
33 "Unable to handle generics",
34 ));
35 }
36 Ok(())
37}
38
39pub(crate) fn embed_generated_code(
40 embed: bool,
41 name: &syn::Ident,
42 output: proc_macro2::TokenStream,
43 macro_out: &str,
44) -> Result<proc_macro2::TokenStream, syn::Error> {
45 let embed = embed
46 || std::env::var("CIDO_ETHEREUM_EMBED_GENERATED_CODE")
47 .map_or(false, |s| s == "1" || s == "true");
48 if embed {
49 let out_dir = std::env::var("OUT_DIR")
50 .or_else(|_| std::env::var("TMPDIR"))
51 .unwrap_or_else(|_| "/tmp".to_string());
52 let out_file = format!("{out_dir}/{name}.{macro_out}.rs");
53 println!("Including {name} instead of generating in {out_file}");
54 write_to_file(out_file.as_ref(), &output).map_err(|e| {
55 syn::Error::new(
56 name.span(),
57 format_args!("Unable to write output to file {out_file}. {e}"),
58 )
59 })?;
60 Ok(quote::quote!(::std::include! {#out_file}))
61 } else {
62 Ok(output)
63 }
64}
65
66pub(crate) fn write_to_file(path: &Path, tokens: &TokenStream) -> syn::Result<()> {
69 let content = tokens.to_string();
70
71 let code = match syn::parse_file(&content) {
72 Ok(output) => {
73 std::panic::catch_unwind(|| prettyplease::unparse(&output)).unwrap_or_else(|panic| {
74 if let Some(s) = panic.downcast_ref::<String>() {
75 eprintln!("prettyplease::unparse panicked. Using raw output. Panic message: {s}");
76 } else {
77 eprintln!("prettyplease::unparse panicked. Using raw output");
78 }
79 content
80 })
81 }
82 Err(e) => {
83 eprintln!("syntax error found. Using raw token stream. Error = {e}");
84 content
85 }
86 };
87
88 write_string_to_file(tokens, path, &code)
89}
90
91pub(crate) fn write_string_to_file(
94 span: &dyn syn::spanned::Spanned,
95 path: &Path,
96 content: &str,
97) -> syn::Result<()> {
98 use std::io::Write;
99 let display_path = path.display();
100
101 match std::fs::read_to_string(path) {
102 Ok(existing_code) if existing_code == content => {}
104 _ => {
105 println!("writing to {display_path}");
106 let mut file = std::fs::File::create(path).map_err(|e| {
107 eprintln!("Unable to create file {display_path}. {e}");
108 syn::Error::new(span.span(), e)
109 })?;
110 file.write_all(content.as_bytes()).map_err(|e| {
111 eprintln!("Unable to write to file {display_path}. {e}");
112 syn::Error::new(span.span(), e)
113 })?;
114 file.sync_all().map_err(|e| {
115 eprintln!("Unable to sync file {display_path}. {e}");
116 syn::Error::new(span.span(), e)
117 })?;
118 }
119 }
120 Ok(())
121}
122
123#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Default)]
124#[repr(u8)]
125enum ProcessingOrder {
126 #[default]
127 One = 1,
128 Two = 2,
129 Three = 3,
130}
131
132impl From<ProcessingOrder> for u8 {
133 fn from(val: ProcessingOrder) -> Self {
134 val as u8
135 }
136}
137
138impl TryFrom<u8> for ProcessingOrder {
139 type Error = u8;
140
141 fn try_from(value: u8) -> Result<Self, Self::Error> {
142 match value {
143 1 => Ok(ProcessingOrder::One),
144 2 => Ok(ProcessingOrder::Two),
145 3 => Ok(ProcessingOrder::Three),
146 val => Err(val),
147 }
148 }
149}
150
151impl deluxe::ParseMetaItem for ProcessingOrder {
152 fn parse_meta_item(
153 input: syn::parse::ParseStream,
154 _mode: deluxe::ParseMode,
155 ) -> deluxe::Result<Self> {
156 let u = input.parse::<syn::LitInt>()?;
157 let u = u.base10_parse::<u8>()?;
158 Self::try_from(u).map_err(|_| crate::err(&u, "Invalid order"))
159 }
160}