1use std::fs::File;
2use std::io::Write;
3use std::path::PathBuf;
4use std::{env, fs};
5
6use anyhow::{anyhow, Result};
7use convert_case::{Case, Casing};
8use quote::format_ident;
9
10use unc_abi_client_impl::{generate_abi_client, read_abi};
11pub use unc_abi_client_macros::generate;
12
13#[derive(Default)]
15pub struct Generator {
16 pub out_dir: Option<PathBuf>,
17 abis: Vec<(PathBuf, Option<String>)>,
18}
19
20impl Generator {
21 pub fn new(out_dir: PathBuf) -> Self {
22 Generator {
23 out_dir: Some(out_dir),
24 abis: vec![],
25 }
26 }
27
28 pub fn file(mut self, path: impl Into<PathBuf>) -> Self {
29 self.abis.push((path.into(), None));
30 self
31 }
32
33 pub fn file_with_name(mut self, path: impl Into<PathBuf>, name: String) -> Self {
34 self.abis.push((path.into(), Some(name)));
35 self
36 }
37
38 pub fn generate(self) -> Result<()> {
39 let target: PathBuf = self.out_dir.clone().map(Ok).unwrap_or_else(|| {
40 env::var_os("OUT_DIR")
41 .ok_or_else(|| anyhow!("OUT_DIR environment variable is not set"))
42 .map(Into::into)
43 })?;
44 fs::create_dir_all(&target)?;
45
46 for (abi_path, name) in self.abis {
47 let abi_path_no_ext = abi_path.with_extension("");
48 let abi_filename = abi_path_no_ext
49 .file_name()
50 .ok_or_else(|| anyhow!("{:?} is not a valid ABI path", &abi_path))?;
51 let rust_path = target.join(abi_filename).with_extension("rs");
52 let unc_abi = read_abi(&abi_path);
53
54 let contract_name = name
55 .as_ref()
56 .map(|n| format_ident!("{}", n))
57 .or_else(|| {
58 unc_abi
59 .metadata
60 .name
61 .clone()
62 .map(|n| format_ident!("{}Client", n.to_case(Case::UpperCamel)))
63 })
64 .ok_or_else(|| {
65 anyhow!(
66 "ABI file '{}' does not contain a contract name. Please supply the name via `file_with_name`.",
67 abi_path.display()
68 )
69 })?;
70
71 let token_stream = generate_abi_client(unc_abi, contract_name);
72 let syntax_tree = syn::parse_file(&token_stream.to_string()).unwrap();
73 let formatted = prettyplease::unparse(&syntax_tree);
74
75 let mut rust_file = File::create(rust_path)?;
76 write!(rust_file, "{}", formatted)?;
77 }
78
79 Ok(())
80 }
81}