feat(abigen): use prettyplease (#2027)

This commit is contained in:
DaniPopes 2023-01-09 06:17:22 +01:00 committed by GitHub
parent c439566625
commit 0187bedd11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 75 deletions

11
Cargo.lock generated
View File

@ -1378,6 +1378,7 @@ dependencies = [
"eyre", "eyre",
"getrandom 0.2.8", "getrandom 0.2.8",
"hex", "hex",
"prettyplease",
"proc-macro2", "proc-macro2",
"quote", "quote",
"regex", "regex",
@ -3162,6 +3163,16 @@ dependencies = [
"yansi", "yansi",
] ]
[[package]]
name = "prettyplease"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78"
dependencies = [
"proc-macro2",
"syn",
]
[[package]] [[package]]
name = "primitive-types" name = "primitive-types"
version = "0.12.1" version = "0.12.1"

View File

@ -16,10 +16,12 @@ keywords = ["ethereum", "web3", "celo", "ethers"]
[dependencies] [dependencies]
ethers-core = { version = "^1.0.0", path = "../../ethers-core", features = ["macros"] } ethers-core = { version = "^1.0.0", path = "../../ethers-core", features = ["macros"] }
Inflector = "0.11"
proc-macro2 = "1.0" proc-macro2 = "1.0"
quote = "1.0" quote = "1.0"
syn = "1.0.12" syn = { version = "1.0.12", default-features = false, features = ["full"] }
prettyplease = "0.1.23"
Inflector = "0.11"
url = "2.1" url = "2.1"
serde_json = "1.0.61" serde_json = "1.0.61"
serde = { version = "1.0.124", features = ["derive"] } serde = { version = "1.0.124", features = ["derive"] }

View File

@ -16,7 +16,6 @@ pub mod contract;
pub use contract::structs::InternalStructs; pub use contract::structs::InternalStructs;
use contract::Context; use contract::Context;
mod rustfmt;
mod source; mod source;
mod util; mod util;
@ -69,8 +68,8 @@ pub struct Abigen {
/// Derives added to event structs and enums. /// Derives added to event structs and enums.
event_derives: Vec<String>, event_derives: Vec<String>,
/// Format the code using a locally installed copy of `rustfmt`. /// Whether to format the code. Uses [`prettyplease`].
rustfmt: bool, format: bool,
/// Manually specified event name aliases. /// Manually specified event name aliases.
event_aliases: HashMap<String, String>, event_aliases: HashMap<String, String>,
@ -89,7 +88,7 @@ impl Abigen {
method_aliases: HashMap::new(), method_aliases: HashMap::new(),
event_derives: Vec::new(), event_derives: Vec::new(),
event_aliases: HashMap::new(), event_aliases: HashMap::new(),
rustfmt: true, format: true,
error_aliases: Default::default(), error_aliases: Default::default(),
}) })
} }
@ -147,14 +146,20 @@ impl Abigen {
self self
} }
/// Specify whether or not to format the code using a locally installed copy
/// of `rustfmt`.
///
/// Note that in case `rustfmt` does not exist or produces an error, the
/// unformatted code will be used.
#[must_use] #[must_use]
#[deprecated = "Use format instead"]
#[doc(hidden)]
pub fn rustfmt(mut self, rustfmt: bool) -> Self { pub fn rustfmt(mut self, rustfmt: bool) -> Self {
self.rustfmt = rustfmt; self.format = rustfmt;
self
}
/// Specify whether to format the code or not. True by default.
///
/// This will use [`prettyplease`], so the resulting formatted code **will not** be affected by
/// the local `rustfmt` version or config.
pub fn format(mut self, format: bool) -> Self {
self.format = format;
self self
} }
@ -173,10 +178,10 @@ impl Abigen {
/// Generates the contract bindings. /// Generates the contract bindings.
pub fn generate(self) -> Result<ContractBindings> { pub fn generate(self) -> Result<ContractBindings> {
let rustfmt = self.rustfmt; let format = self.format;
let name = self.contract_name.clone(); let name = self.contract_name.clone();
let (expanded, _) = self.expand()?; let (expanded, _) = self.expand()?;
Ok(ContractBindings { tokens: expanded.into_tokens(), rustfmt, name }) Ok(ContractBindings { tokens: expanded.into_tokens(), format, name })
} }
/// Expands the `Abigen` and returns the [`ExpandedContract`] that holds all tokens and the /// Expands the `Abigen` and returns the [`ExpandedContract`] that holds all tokens and the
@ -193,7 +198,7 @@ pub struct ContractBindings {
/// The TokenStream representing the contract bindings. /// The TokenStream representing the contract bindings.
tokens: TokenStream, tokens: TokenStream,
/// The output options used for serialization. /// The output options used for serialization.
rustfmt: bool, format: bool,
/// The contract name /// The contract name
name: String, name: String,
} }
@ -204,14 +209,11 @@ impl ContractBindings {
where where
W: Write, W: Write,
{ {
let source = { let source = if self.format {
let raw = self.tokens.to_string(); let syntax_tree = syn::parse2::<syn::File>(self.tokens.clone()).unwrap();
prettyplease::unparse(&syntax_tree)
if self.rustfmt {
rustfmt::format(&raw).unwrap_or(raw)
} else { } else {
raw self.tokens.to_string()
}
}; };
w.write_all(source.as_bytes())?; w.write_all(source.as_bytes())?;

View File

@ -139,11 +139,8 @@ impl MultiAbigen {
/// Build the contract bindings and prepare for writing /// Build the contract bindings and prepare for writing
pub fn build(self) -> Result<MultiBindings> { pub fn build(self) -> Result<MultiBindings> {
let rustfmt = self.abigens.iter().any(|gen| gen.rustfmt); let format = self.abigens.iter().any(|gen| gen.format);
Ok(MultiBindings { Ok(MultiBindings { expansion: MultiExpansion::from_abigen(self.abigens)?.expand(), format })
expansion: MultiExpansion::from_abigen(self.abigens)?.expand(),
rustfmt,
})
} }
} }
@ -298,14 +295,14 @@ impl MultiExpansionResult {
} }
/// Converts this result into [`MultiBindingsInner`] /// Converts this result into [`MultiBindingsInner`]
fn into_bindings(mut self, single_file: bool, rustfmt: bool) -> MultiBindingsInner { fn into_bindings(mut self, single_file: bool, format: bool) -> MultiBindingsInner {
self.set_shared_import_path(single_file); self.set_shared_import_path(single_file);
let Self { contracts, shared_types, root, .. } = self; let Self { contracts, shared_types, root, .. } = self;
let bindings = contracts let bindings = contracts
.into_iter() .into_iter()
.map(|(expanded, ctx)| ContractBindings { .map(|(expanded, ctx)| ContractBindings {
tokens: expanded.into_tokens(), tokens: expanded.into_tokens(),
rustfmt, format,
name: ctx.contract_name().to_string(), name: ctx.contract_name().to_string(),
}) })
.map(|v| (v.name.clone(), v)) .map(|v| (v.name.clone(), v))
@ -325,7 +322,7 @@ impl MultiExpansionResult {
}; };
Some(ContractBindings { Some(ContractBindings {
tokens: shared_types, tokens: shared_types,
rustfmt, format,
name: "shared_types".to_string(), name: "shared_types".to_string(),
}) })
} else { } else {
@ -364,7 +361,7 @@ impl MultiExpansionResult {
/// changed) /// changed)
pub struct MultiBindings { pub struct MultiBindings {
expansion: MultiExpansionResult, expansion: MultiExpansionResult,
rustfmt: bool, format: bool,
} }
impl MultiBindings { impl MultiBindings {
@ -378,18 +375,25 @@ impl MultiBindings {
self.expansion.contracts.is_empty() self.expansion.contracts.is_empty()
} }
/// Specify whether or not to format the code using a locally installed copy #[must_use]
/// of `rustfmt`. #[deprecated = "Use format instead"]
/// #[doc(hidden)]
/// Note that in case `rustfmt` does not exist or produces an error, the
/// unformatted code will be used.
pub fn rustfmt(mut self, rustfmt: bool) -> Self { pub fn rustfmt(mut self, rustfmt: bool) -> Self {
self.rustfmt = rustfmt; self.format = rustfmt;
self
}
/// Specify whether to format the code or not. True by default.
///
/// This will use [`prettyplease`], so the resulting formatted code **will not** be affected by
/// the local `rustfmt` version or config.
pub fn format(mut self, format: bool) -> Self {
self.format = format;
self self
} }
fn into_inner(self, single_file: bool) -> MultiBindingsInner { fn into_inner(self, single_file: bool) -> MultiBindingsInner {
self.expansion.into_bindings(single_file, self.rustfmt) self.expansion.into_bindings(single_file, self.format)
} }
/// Generates all the bindings and writes them to the given module /// Generates all the bindings and writes them to the given module

View File

@ -1,36 +0,0 @@
//! This module implements basic `rustfmt` code formatting.
use eyre::{eyre, Result};
use std::{
io::Write,
process::{Command, Stdio},
};
/// Format the raw input source string and return formatted output.
pub fn format<S>(source: S) -> Result<String>
where
S: AsRef<str>,
{
let mut rustfmt =
Command::new("rustfmt").stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?;
{
let stdin = rustfmt
.stdin
.as_mut()
.ok_or_else(|| eyre!("stdin was not created for `rustfmt` child process"))?;
stdin.write_all(source.as_ref().as_bytes())?;
}
let output = rustfmt.wait_with_output()?;
eyre::ensure!(
output.status.success(),
"`rustfmt` exited with code {}:\n{}",
output.status,
String::from_utf8_lossy(&output.stderr),
);
let stdout = String::from_utf8(output.stdout)?;
Ok(stdout)
}