refactor/feat(abigen,types): add bytes::Bytes static methods, refactor struct declaration (#2089)

* refactor: final struct expansion

* feat(types): implement bytes::Bytes static methods

* feat: use static Bytes for bytecode

* chore: add rustfmt skip directive

* clippy
This commit is contained in:
DaniPopes 2023-01-30 21:12:35 +01:00 committed by GitHub
parent 71c6fd4928
commit 93e1850646
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 39 deletions

View File

@ -1,11 +1,9 @@
use super::{util, Context}; use super::{types, util, Context};
use crate::contract::types;
use ethers_core::{ use ethers_core::{
abi::{Param, ParamType}, abi::{Param, ParamType},
macros::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate}, macros::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate},
}; };
use proc_macro2::{Ident, TokenStream}; use proc_macro2::{Ident, Literal, TokenStream};
use quote::quote; use quote::quote;
/// Expands to the `name : type` pairs for the params /// Expands to the `name : type` pairs for the params
@ -87,49 +85,56 @@ pub(crate) fn imports(name: &str) -> TokenStream {
} }
} }
/// Generates the static `Abi` constants and the contract struct /// Generates the token stream for the contract's ABI, bytecode and struct declarations.
pub(crate) fn struct_declaration(cx: &Context) -> TokenStream { pub(crate) fn struct_declaration(cx: &Context) -> TokenStream {
let name = &cx.contract_ident; let name = &cx.contract_ident;
let abi = &cx.abi_str;
let abi_name = cx.inline_abi_ident();
let ethers_core = ethers_core_crate(); let ethers_core = ethers_core_crate();
let ethers_contract = ethers_contract_crate(); let ethers_contract = ethers_contract_crate();
let abi_parse = if !cx.human_readable { let abi = {
let abi_name = cx.inline_abi_ident();
let abi = &cx.abi_str;
let (doc_str, parse) = if cx.human_readable {
// Human readable: use abi::parse_abi_str
let doc_str = "The parsed human-readable ABI of the contract.";
let parse = quote!(#ethers_core::abi::parse_abi_str(__ABI));
(doc_str, parse)
} else {
// JSON ABI: use serde_json::from_str
let doc_str = "The parsed JSON ABI of the contract.";
let parse = quote!(#ethers_core::utils::__serde_json::from_str(__ABI));
(doc_str, parse)
};
quote! { quote! {
#[rustfmt::skip] #[rustfmt::skip]
const __ABI: &str = #abi; const __ABI: &str = #abi;
/// The parsed JSON-ABI of the contract. // This never fails as we are parsing the ABI in this macro
pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> = #ethers_contract::Lazy::new(|| #ethers_core::utils::__serde_json::from_str(__ABI) #[doc = #doc_str]
.expect("invalid abi")); pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> =
} #ethers_contract::Lazy::new(|| #parse.expect("ABI is always valid"));
} else {
quote! {
/// The parsed human readable ABI of the contract.
pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> = #ethers_contract::Lazy::new(|| #ethers_core::abi::parse_abi_str(#abi)
.expect("invalid abi"));
} }
}; };
let bytecode = if let Some(ref bytecode) = cx.contract_bytecode { let bytecode = cx.contract_bytecode.as_ref().map(|bytecode| {
let bytecode = bytecode.iter().copied().map(Literal::u8_unsuffixed);
let bytecode_name = cx.inline_bytecode_ident(); let bytecode_name = cx.inline_bytecode_ident();
let hex_bytecode = format!("{bytecode}");
quote! { quote! {
/// Bytecode of the #name contract #[rustfmt::skip]
pub static #bytecode_name: #ethers_contract::Lazy<#ethers_core::types::Bytes> = #ethers_contract::Lazy::new(|| #hex_bytecode.parse() const __BYTECODE: &[u8] = &[ #( #bytecode ),* ];
.expect("invalid bytecode"));
#[doc = "The bytecode of the contract."]
pub static #bytecode_name: #ethers_core::types::Bytes = #ethers_core::types::Bytes::from_static(__BYTECODE);
} }
} else { });
quote! {}
};
quote! { quote! {
// Inline ABI declaration // The `Lazy` ABI
#abi_parse #abi
// The static Bytecode, if present
#bytecode #bytecode
// Struct declaration // Struct declaration

View File

@ -16,31 +16,62 @@ pub struct Bytes(
pub bytes::Bytes, pub bytes::Bytes,
); );
fn bytes_to_hex(b: &Bytes) -> String { impl Bytes {
hex::encode(b.0.as_ref()) /// Creates a new empty `Bytes`.
///
/// This will not allocate and the returned `Bytes` handle will be empty.
///
/// # Examples
///
/// ```
/// use ethers_core::types::Bytes;
///
/// let b = Bytes::new();
/// assert_eq!(&b[..], b"");
/// ```
#[inline]
pub const fn new() -> Self {
Self(bytes::Bytes::new())
}
/// Creates a new `Bytes` from a static slice.
///
/// The returned `Bytes` will point directly to the static slice. There is
/// no allocating or copying.
///
/// # Examples
///
/// ```
/// use ethers_core::types::Bytes;
///
/// let b = Bytes::from_static(b"hello");
/// assert_eq!(&b[..], b"hello");
/// ```
#[inline]
pub const fn from_static(bytes: &'static [u8]) -> Self {
Self(bytes::Bytes::from_static(bytes))
}
fn hex_encode(&self) -> String {
hex::encode(self.0.as_ref())
}
} }
impl Debug for Bytes { impl Debug for Bytes {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "Bytes(0x{})", bytes_to_hex(self)) write!(f, "Bytes(0x{})", self.hex_encode())
} }
} }
impl Display for Bytes { impl Display for Bytes {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "0x{}", bytes_to_hex(self)) write!(f, "0x{}", self.hex_encode())
} }
} }
impl LowerHex for Bytes { impl LowerHex for Bytes {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "0x{}", bytes_to_hex(self)) write!(f, "0x{}", self.hex_encode())
}
}
impl Bytes {
pub fn to_vec(&self) -> Vec<u8> {
self.as_ref().to_vec()
} }
} }