feat(solc): more options for output selection (#898)
* refactor: make artifacts a sub mod * feat: more options for output selection * chore: use tostring instead
This commit is contained in:
parent
8f19ba1fa4
commit
cdaac16f0a
|
@ -22,6 +22,10 @@ use crate::{
|
||||||
use ethers_core::abi::Address;
|
use ethers_core::abi::Address;
|
||||||
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
pub mod output_selection;
|
||||||
|
pub mod serde_helpers;
|
||||||
|
pub use serde_helpers::{deserialize_bytes, deserialize_opt_bytes};
|
||||||
|
|
||||||
/// Solidity files are made up of multiple `source units`, a solidity contract is such a `source
|
/// Solidity files are made up of multiple `source units`, a solidity contract is such a `source
|
||||||
/// unit`, therefore a solidity file can contain multiple contracts: (1-N*) relationship.
|
/// unit`, therefore a solidity file can contain multiple contracts: (1-N*) relationship.
|
||||||
///
|
///
|
||||||
|
@ -180,18 +184,40 @@ pub struct Settings {
|
||||||
/// ```
|
/// ```
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub output_selection: BTreeMap<String, BTreeMap<String, Vec<String>>>,
|
pub output_selection: BTreeMap<String, BTreeMap<String, Vec<String>>>,
|
||||||
#[serde(default, with = "display_from_str_opt", skip_serializing_if = "Option::is_none")]
|
#[serde(
|
||||||
|
default,
|
||||||
|
with = "serde_helpers::display_from_str_opt",
|
||||||
|
skip_serializing_if = "Option::is_none"
|
||||||
|
)]
|
||||||
pub evm_version: Option<EvmVersion>,
|
pub evm_version: Option<EvmVersion>,
|
||||||
#[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")]
|
#[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")]
|
||||||
pub libraries: BTreeMap<String, BTreeMap<String, String>>,
|
pub libraries: BTreeMap<String, BTreeMap<String, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
|
/// Creates a new `Settings` instance with the given `output_selection`
|
||||||
|
pub fn new(output_selection: BTreeMap<String, BTreeMap<String, Vec<String>>>) -> Self {
|
||||||
|
Self { output_selection, ..Default::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// select all outputs the compiler can possibly generate, use
|
||||||
|
/// `{ "*": { "*": [ "*" ], "": [ "*" ] } }`
|
||||||
|
/// but note that this might slow down the compilation process needlessly.
|
||||||
|
pub fn complete_output_selection() -> BTreeMap<String, BTreeMap<String, Vec<String>>> {
|
||||||
|
BTreeMap::from([(
|
||||||
|
"*".to_string(),
|
||||||
|
BTreeMap::from([
|
||||||
|
("*".to_string(), vec!["*".to_string()]),
|
||||||
|
("".to_string(), vec!["*".to_string()]),
|
||||||
|
]),
|
||||||
|
)])
|
||||||
|
}
|
||||||
|
|
||||||
/// Default output selection for compiler output
|
/// Default output selection for compiler output
|
||||||
pub fn default_output_selection() -> BTreeMap<String, BTreeMap<String, Vec<String>>> {
|
pub fn default_output_selection() -> BTreeMap<String, BTreeMap<String, Vec<String>>> {
|
||||||
let mut output_selection = BTreeMap::default();
|
BTreeMap::from([(
|
||||||
let mut output = BTreeMap::default();
|
"*".to_string(),
|
||||||
output.insert(
|
BTreeMap::from([(
|
||||||
"*".to_string(),
|
"*".to_string(),
|
||||||
vec![
|
vec![
|
||||||
"abi".to_string(),
|
"abi".to_string(),
|
||||||
|
@ -199,13 +225,19 @@ impl Settings {
|
||||||
"evm.deployedBytecode".to_string(),
|
"evm.deployedBytecode".to_string(),
|
||||||
"evm.methodIdentifiers".to_string(),
|
"evm.methodIdentifiers".to_string(),
|
||||||
],
|
],
|
||||||
);
|
)]),
|
||||||
output_selection.insert("*".to_string(), output);
|
)])
|
||||||
output_selection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts the value for all files and contracts
|
/// Inserts the value for all files and contracts
|
||||||
pub fn push_output_selection(&mut self, value: impl Into<String>) {
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ethers_solc::artifacts::output_selection::ContractOutputSelection;
|
||||||
|
/// use ethers_solc::artifacts::Settings;
|
||||||
|
/// let mut selection = Settings::default();
|
||||||
|
/// selection.push_output_selection(ContractOutputSelection::Metadata);
|
||||||
|
/// ```
|
||||||
|
pub fn push_output_selection(&mut self, value: impl ToString) {
|
||||||
self.push_contract_output_selection("*", value)
|
self.push_contract_output_selection("*", value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,9 +247,9 @@ impl Settings {
|
||||||
pub fn push_contract_output_selection(
|
pub fn push_contract_output_selection(
|
||||||
&mut self,
|
&mut self,
|
||||||
contracts: impl Into<String>,
|
contracts: impl Into<String>,
|
||||||
value: impl Into<String>,
|
value: impl ToString,
|
||||||
) {
|
) {
|
||||||
let value = value.into();
|
let value = value.to_string();
|
||||||
let values = self
|
let values = self
|
||||||
.output_selection
|
.output_selection
|
||||||
.entry("*".to_string())
|
.entry("*".to_string())
|
||||||
|
@ -230,7 +262,7 @@ impl Settings {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the value for all files and contracts
|
/// Sets the value for all files and contracts
|
||||||
pub fn set_output_selection(&mut self, values: impl IntoIterator<Item = impl Into<String>>) {
|
pub fn set_output_selection(&mut self, values: impl IntoIterator<Item = impl ToString>) {
|
||||||
self.set_contract_output_selection("*", values)
|
self.set_contract_output_selection("*", values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,12 +272,12 @@ impl Settings {
|
||||||
pub fn set_contract_output_selection(
|
pub fn set_contract_output_selection(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: impl Into<String>,
|
key: impl Into<String>,
|
||||||
values: impl IntoIterator<Item = impl Into<String>>,
|
values: impl IntoIterator<Item = impl ToString>,
|
||||||
) {
|
) {
|
||||||
self.output_selection
|
self.output_selection
|
||||||
.entry("*".to_string())
|
.entry("*".to_string())
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(key.into(), values.into_iter().map(Into::into).collect());
|
.insert(key.into(), values.into_iter().map(|s| s.to_string()).collect());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds `ast` to output
|
/// Adds `ast` to output
|
||||||
|
@ -792,7 +824,11 @@ pub struct Contract {
|
||||||
/// The Ethereum Contract Metadata.
|
/// The Ethereum Contract Metadata.
|
||||||
/// See https://docs.soliditylang.org/en/develop/metadata.html
|
/// See https://docs.soliditylang.org/en/develop/metadata.html
|
||||||
pub abi: Option<Abi>,
|
pub abi: Option<Abi>,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none", with = "json_string_opt")]
|
#[serde(
|
||||||
|
default,
|
||||||
|
skip_serializing_if = "Option::is_none",
|
||||||
|
with = "serde_helpers::json_string_opt"
|
||||||
|
)]
|
||||||
pub metadata: Option<Metadata>,
|
pub metadata: Option<Metadata>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub userdoc: UserDoc,
|
pub userdoc: UserDoc,
|
||||||
|
@ -1488,7 +1524,7 @@ impl Bytecode {
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum BytecodeObject {
|
pub enum BytecodeObject {
|
||||||
/// Fully linked bytecode object
|
/// Fully linked bytecode object
|
||||||
#[serde(deserialize_with = "deserialize_bytes")]
|
#[serde(deserialize_with = "serde_helpers::deserialize_bytes")]
|
||||||
Bytecode(Bytes),
|
Bytecode(Bytes),
|
||||||
/// Bytecode as hex string that's not fully linked yet and contains library placeholders
|
/// Bytecode as hex string that's not fully linked yet and contains library placeholders
|
||||||
Unlinked(String),
|
Unlinked(String),
|
||||||
|
@ -1738,7 +1774,7 @@ pub struct Ewasm {
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
pub struct StorageLayout {
|
pub struct StorageLayout {
|
||||||
pub storage: Vec<Storage>,
|
pub storage: Vec<Storage>,
|
||||||
#[serde(default, deserialize_with = "default_for_null")]
|
#[serde(default, deserialize_with = "serde_helpers::default_for_null")]
|
||||||
pub types: BTreeMap<String, StorageType>,
|
pub types: BTreeMap<String, StorageType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1778,7 +1814,7 @@ pub struct Error {
|
||||||
pub r#type: String,
|
pub r#type: String,
|
||||||
pub component: String,
|
pub component: String,
|
||||||
pub severity: Severity,
|
pub severity: Severity,
|
||||||
#[serde(default, with = "display_from_str_opt")]
|
#[serde(default, with = "serde_helpers::display_from_str_opt")]
|
||||||
pub error_code: Option<u64>,
|
pub error_code: Option<u64>,
|
||||||
pub message: String,
|
pub message: String,
|
||||||
pub formatted_message: Option<String>,
|
pub formatted_message: Option<String>,
|
||||||
|
@ -1935,110 +1971,6 @@ impl SourceFiles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod display_from_str_opt {
|
|
||||||
use serde::{de, Deserialize, Deserializer, Serializer};
|
|
||||||
use std::{fmt, str::FromStr};
|
|
||||||
|
|
||||||
pub fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
T: fmt::Display,
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
if let Some(value) = value {
|
|
||||||
serializer.collect_str(value)
|
|
||||||
} else {
|
|
||||||
serializer.serialize_none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
T: FromStr,
|
|
||||||
T::Err: fmt::Display,
|
|
||||||
{
|
|
||||||
if let Some(s) = Option::<String>::deserialize(deserializer)? {
|
|
||||||
s.parse().map_err(de::Error::custom).map(Some)
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod json_string_opt {
|
|
||||||
use serde::{
|
|
||||||
de::{self, DeserializeOwned},
|
|
||||||
ser, Deserialize, Deserializer, Serialize, Serializer,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
T: Serialize,
|
|
||||||
{
|
|
||||||
if let Some(value) = value {
|
|
||||||
let value = serde_json::to_string(value).map_err(ser::Error::custom)?;
|
|
||||||
serializer.serialize_str(&value)
|
|
||||||
} else {
|
|
||||||
serializer.serialize_none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
T: DeserializeOwned,
|
|
||||||
{
|
|
||||||
if let Some(s) = Option::<String>::deserialize(deserializer)? {
|
|
||||||
serde_json::from_str(&s).map_err(de::Error::custom).map(Some)
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize_bytes<'de, D>(d: D) -> std::result::Result<Bytes, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let value = String::deserialize(d)?;
|
|
||||||
if let Some(value) = value.strip_prefix("0x") {
|
|
||||||
hex::decode(value)
|
|
||||||
} else {
|
|
||||||
hex::decode(&value)
|
|
||||||
}
|
|
||||||
.map(Into::into)
|
|
||||||
.map_err(|e| serde::de::Error::custom(e.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize_opt_bytes<'de, D>(d: D) -> std::result::Result<Option<Bytes>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let value = Option::<String>::deserialize(d)?;
|
|
||||||
if let Some(value) = value {
|
|
||||||
Ok(Some(
|
|
||||||
if let Some(value) = value.strip_prefix("0x") {
|
|
||||||
hex::decode(value)
|
|
||||||
} else {
|
|
||||||
hex::decode(&value)
|
|
||||||
}
|
|
||||||
.map_err(|e| serde::de::Error::custom(e.to_string()))?
|
|
||||||
.into(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_for_null<'de, D, T>(deserializer: D) -> Result<T, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
T: Deserialize<'de> + Default,
|
|
||||||
{
|
|
||||||
Ok(Option::<T>::deserialize(deserializer)?.unwrap_or_default())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
|
@ -0,0 +1,352 @@
|
||||||
|
//! bindings for standard json output selection
|
||||||
|
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
|
/// Contract level output selection
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub enum ContractOutputSelection {
|
||||||
|
Abi,
|
||||||
|
DevDoc,
|
||||||
|
UserDoc,
|
||||||
|
Metadata,
|
||||||
|
Ir,
|
||||||
|
IrOptimized,
|
||||||
|
StorageLayout,
|
||||||
|
Evm(EvmOutputSelection),
|
||||||
|
Ewasm(EwasmOutputSelection),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for ContractOutputSelection {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.collect_str(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for ContractOutputSelection {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ContractOutputSelection {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ContractOutputSelection::Abi => f.write_str("abi"),
|
||||||
|
ContractOutputSelection::DevDoc => f.write_str("devdoc"),
|
||||||
|
ContractOutputSelection::UserDoc => f.write_str("userdoc"),
|
||||||
|
ContractOutputSelection::Metadata => f.write_str("metadata"),
|
||||||
|
ContractOutputSelection::Ir => f.write_str("ir"),
|
||||||
|
ContractOutputSelection::IrOptimized => f.write_str("irOptimized"),
|
||||||
|
ContractOutputSelection::StorageLayout => f.write_str("storageLayout"),
|
||||||
|
ContractOutputSelection::Evm(e) => e.fmt(f),
|
||||||
|
ContractOutputSelection::Ewasm(e) => e.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ContractOutputSelection {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"abi" => Ok(ContractOutputSelection::Abi),
|
||||||
|
"devdoc" => Ok(ContractOutputSelection::DevDoc),
|
||||||
|
"userdoc" => Ok(ContractOutputSelection::UserDoc),
|
||||||
|
"metadata" => Ok(ContractOutputSelection::Metadata),
|
||||||
|
"ir" => Ok(ContractOutputSelection::Ir),
|
||||||
|
"irOptimized" => Ok(ContractOutputSelection::IrOptimized),
|
||||||
|
"storageLayout" => Ok(ContractOutputSelection::StorageLayout),
|
||||||
|
s => EvmOutputSelection::from_str(s)
|
||||||
|
.map(ContractOutputSelection::Evm)
|
||||||
|
.or_else(|_| EwasmOutputSelection::from_str(s).map(ContractOutputSelection::Ewasm))
|
||||||
|
.map_err(|_| format!("Invalid contract output selection: {}", s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contract level output selection for `evm`
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub enum EvmOutputSelection {
|
||||||
|
All,
|
||||||
|
Assembly,
|
||||||
|
LegacyAssembly,
|
||||||
|
MethodIdentifiers,
|
||||||
|
GasEstimates,
|
||||||
|
ByteCode(BytecodeOutputSelection),
|
||||||
|
DeployedByteCode(DeployedBytecodeOutputSelection),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for EvmOutputSelection {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.collect_str(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for EvmOutputSelection {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for EvmOutputSelection {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
EvmOutputSelection::All => f.write_str("evm"),
|
||||||
|
EvmOutputSelection::Assembly => f.write_str("evm.assembly"),
|
||||||
|
EvmOutputSelection::LegacyAssembly => f.write_str("evm.legacyAssembly"),
|
||||||
|
EvmOutputSelection::MethodIdentifiers => f.write_str("evm.methodIdentifiers"),
|
||||||
|
EvmOutputSelection::GasEstimates => f.write_str("evm.gasEstimates"),
|
||||||
|
EvmOutputSelection::ByteCode(b) => b.fmt(f),
|
||||||
|
EvmOutputSelection::DeployedByteCode(b) => b.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for EvmOutputSelection {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"evm" => Ok(EvmOutputSelection::All),
|
||||||
|
"evm.assembly" => Ok(EvmOutputSelection::Assembly),
|
||||||
|
"evm.legacyAssembly" => Ok(EvmOutputSelection::LegacyAssembly),
|
||||||
|
"evm.methodIdentifiers" => Ok(EvmOutputSelection::MethodIdentifiers),
|
||||||
|
"evm.gasEstimates" => Ok(EvmOutputSelection::GasEstimates),
|
||||||
|
s => BytecodeOutputSelection::from_str(s)
|
||||||
|
.map(EvmOutputSelection::ByteCode)
|
||||||
|
.or_else(|_| {
|
||||||
|
DeployedBytecodeOutputSelection::from_str(s)
|
||||||
|
.map(EvmOutputSelection::DeployedByteCode)
|
||||||
|
})
|
||||||
|
.map_err(|_| format!("Invalid evm selection: {}", s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contract level output selection for `evm.bytecode`
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub enum BytecodeOutputSelection {
|
||||||
|
All,
|
||||||
|
FunctionDebugData,
|
||||||
|
Object,
|
||||||
|
Opcodes,
|
||||||
|
SourceMap,
|
||||||
|
LinkReferences,
|
||||||
|
GeneratedSources,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for BytecodeOutputSelection {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.collect_str(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for BytecodeOutputSelection {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for BytecodeOutputSelection {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
BytecodeOutputSelection::All => f.write_str("evm.bytecode"),
|
||||||
|
BytecodeOutputSelection::FunctionDebugData => {
|
||||||
|
f.write_str("evm.bytecode.functionDebugData")
|
||||||
|
}
|
||||||
|
BytecodeOutputSelection::Object => f.write_str("evm.bytecode.object"),
|
||||||
|
BytecodeOutputSelection::Opcodes => f.write_str("evm.bytecode.opcodes"),
|
||||||
|
BytecodeOutputSelection::SourceMap => f.write_str("evm.bytecode.sourceMap"),
|
||||||
|
BytecodeOutputSelection::LinkReferences => f.write_str("evm.bytecode.linkReferences"),
|
||||||
|
BytecodeOutputSelection::GeneratedSources => {
|
||||||
|
f.write_str("evm.bytecode.generatedSources")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for BytecodeOutputSelection {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"evm.bytecode" => Ok(BytecodeOutputSelection::All),
|
||||||
|
"evm.bytecode.functionDebugData" => Ok(BytecodeOutputSelection::FunctionDebugData),
|
||||||
|
"evm.bytecode.object" => Ok(BytecodeOutputSelection::Object),
|
||||||
|
"evm.bytecode.opcodes" => Ok(BytecodeOutputSelection::Opcodes),
|
||||||
|
"evm.bytecode.sourceMap" => Ok(BytecodeOutputSelection::SourceMap),
|
||||||
|
"evm.bytecode.linkReferences" => Ok(BytecodeOutputSelection::LinkReferences),
|
||||||
|
"evm.bytecode.generatedSources" => Ok(BytecodeOutputSelection::GeneratedSources),
|
||||||
|
s => Err(format!("Invalid bytecode selection: {}", s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contract level output selection for `evm.deployedBytecode`
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub enum DeployedBytecodeOutputSelection {
|
||||||
|
All,
|
||||||
|
FunctionDebugData,
|
||||||
|
Object,
|
||||||
|
Opcodes,
|
||||||
|
SourceMap,
|
||||||
|
LinkReferences,
|
||||||
|
GeneratedSources,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for DeployedBytecodeOutputSelection {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.collect_str(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for DeployedBytecodeOutputSelection {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DeployedBytecodeOutputSelection {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
DeployedBytecodeOutputSelection::All => f.write_str("evm.deployedBytecode"),
|
||||||
|
DeployedBytecodeOutputSelection::FunctionDebugData => {
|
||||||
|
f.write_str("evm.deployedBytecode.functionDebugData")
|
||||||
|
}
|
||||||
|
DeployedBytecodeOutputSelection::Object => f.write_str("evm.deployedBytecode.object"),
|
||||||
|
DeployedBytecodeOutputSelection::Opcodes => f.write_str("evm.deployedBytecode.opcodes"),
|
||||||
|
DeployedBytecodeOutputSelection::SourceMap => {
|
||||||
|
f.write_str("evm.deployedBytecode.sourceMap")
|
||||||
|
}
|
||||||
|
DeployedBytecodeOutputSelection::LinkReferences => {
|
||||||
|
f.write_str("evm.deployedBytecode.linkReferences")
|
||||||
|
}
|
||||||
|
DeployedBytecodeOutputSelection::GeneratedSources => {
|
||||||
|
f.write_str("evm.deployedBytecode.generatedSources")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for DeployedBytecodeOutputSelection {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"evm.deployedBytecode" => Ok(DeployedBytecodeOutputSelection::All),
|
||||||
|
"evm.deployedBytecode.functionDebugData" => {
|
||||||
|
Ok(DeployedBytecodeOutputSelection::FunctionDebugData)
|
||||||
|
}
|
||||||
|
"evm.deployedBytecode.object" => Ok(DeployedBytecodeOutputSelection::Object),
|
||||||
|
"evm.deployedBytecode.opcodes" => Ok(DeployedBytecodeOutputSelection::Opcodes),
|
||||||
|
"evm.deployedBytecode.sourceMap" => Ok(DeployedBytecodeOutputSelection::SourceMap),
|
||||||
|
"evm.deployedBytecode.linkReferences" => {
|
||||||
|
Ok(DeployedBytecodeOutputSelection::LinkReferences)
|
||||||
|
}
|
||||||
|
"evm.deployedBytecode.generatedSources" => {
|
||||||
|
Ok(DeployedBytecodeOutputSelection::GeneratedSources)
|
||||||
|
}
|
||||||
|
s => Err(format!("Invalid deployedBytecode selection: {}", s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contract level output selection for `evm.ewasm`
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub enum EwasmOutputSelection {
|
||||||
|
All,
|
||||||
|
Wast,
|
||||||
|
Wasm,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for EwasmOutputSelection {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.collect_str(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for EwasmOutputSelection {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for EwasmOutputSelection {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
EwasmOutputSelection::All => f.write_str("ewasm"),
|
||||||
|
EwasmOutputSelection::Wast => f.write_str("ewasm.wast"),
|
||||||
|
EwasmOutputSelection::Wasm => f.write_str("ewasm.wasm"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for EwasmOutputSelection {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"ewasm" => Ok(EwasmOutputSelection::All),
|
||||||
|
"ewasm.wast" => Ok(EwasmOutputSelection::Wast),
|
||||||
|
"ewasm.wasm" => Ok(EwasmOutputSelection::Wasm),
|
||||||
|
s => Err(format!("Invalid ewasm selection: {}", s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn outputselection_serde_works() {
|
||||||
|
let mut output = BTreeMap::default();
|
||||||
|
output.insert(
|
||||||
|
"*".to_string(),
|
||||||
|
vec![
|
||||||
|
"abi".to_string(),
|
||||||
|
"evm.bytecode".to_string(),
|
||||||
|
"evm.deployedBytecode".to_string(),
|
||||||
|
"evm.methodIdentifiers".to_string(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&output).unwrap();
|
||||||
|
let deserde_selection: BTreeMap<String, Vec<ContractOutputSelection>> =
|
||||||
|
serde_json::from_str(&json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(json, serde_json::to_string(&deserde_selection).unwrap());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
//! serde helpers
|
||||||
|
|
||||||
|
use ethers_core::types::Bytes;
|
||||||
|
use serde::{Deserialize, Deserializer};
|
||||||
|
|
||||||
|
pub fn deserialize_bytes<'de, D>(d: D) -> std::result::Result<Bytes, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let value = String::deserialize(d)?;
|
||||||
|
if let Some(value) = value.strip_prefix("0x") {
|
||||||
|
hex::decode(value)
|
||||||
|
} else {
|
||||||
|
hex::decode(&value)
|
||||||
|
}
|
||||||
|
.map(Into::into)
|
||||||
|
.map_err(|e| serde::de::Error::custom(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_opt_bytes<'de, D>(d: D) -> std::result::Result<Option<Bytes>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let value = Option::<String>::deserialize(d)?;
|
||||||
|
if let Some(value) = value {
|
||||||
|
Ok(Some(
|
||||||
|
if let Some(value) = value.strip_prefix("0x") {
|
||||||
|
hex::decode(value)
|
||||||
|
} else {
|
||||||
|
hex::decode(&value)
|
||||||
|
}
|
||||||
|
.map_err(|e| serde::de::Error::custom(e.to_string()))?
|
||||||
|
.into(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_for_null<'de, D, T>(deserializer: D) -> Result<T, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
T: Deserialize<'de> + Default,
|
||||||
|
{
|
||||||
|
Ok(Option::<T>::deserialize(deserializer)?.unwrap_or_default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod json_string_opt {
|
||||||
|
use serde::{
|
||||||
|
de::{self, DeserializeOwned},
|
||||||
|
ser, Deserialize, Deserializer, Serialize, Serializer,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
if let Some(value) = value {
|
||||||
|
let value = serde_json::to_string(value).map_err(ser::Error::custom)?;
|
||||||
|
serializer.serialize_str(&value)
|
||||||
|
} else {
|
||||||
|
serializer.serialize_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
T: DeserializeOwned,
|
||||||
|
{
|
||||||
|
if let Some(s) = Option::<String>::deserialize(deserializer)? {
|
||||||
|
serde_json::from_str(&s).map_err(de::Error::custom).map(Some)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod display_from_str_opt {
|
||||||
|
use serde::{de, Deserialize, Deserializer, Serializer};
|
||||||
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
|
pub fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
T: fmt::Display,
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
if let Some(value) = value {
|
||||||
|
serializer.collect_str(value)
|
||||||
|
} else {
|
||||||
|
serializer.serialize_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
T: FromStr,
|
||||||
|
T::Err: fmt::Display,
|
||||||
|
{
|
||||||
|
if let Some(s) = Option::<String>::deserialize(deserializer)? {
|
||||||
|
s.parse().map_err(de::Error::custom).map(Some)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue