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 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
|
||||
/// unit`, therefore a solidity file can contain multiple contracts: (1-N*) relationship.
|
||||
///
|
||||
|
@ -180,32 +184,60 @@ pub struct Settings {
|
|||
/// ```
|
||||
#[serde(default)]
|
||||
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>,
|
||||
#[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")]
|
||||
pub libraries: BTreeMap<String, BTreeMap<String, String>>,
|
||||
}
|
||||
|
||||
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
|
||||
pub fn default_output_selection() -> BTreeMap<String, BTreeMap<String, Vec<String>>> {
|
||||
let mut output_selection = BTreeMap::default();
|
||||
let mut output = BTreeMap::default();
|
||||
output.insert(
|
||||
BTreeMap::from([(
|
||||
"*".to_string(),
|
||||
vec![
|
||||
"abi".to_string(),
|
||||
"evm.bytecode".to_string(),
|
||||
"evm.deployedBytecode".to_string(),
|
||||
"evm.methodIdentifiers".to_string(),
|
||||
],
|
||||
);
|
||||
output_selection.insert("*".to_string(), output);
|
||||
output_selection
|
||||
BTreeMap::from([(
|
||||
"*".to_string(),
|
||||
vec![
|
||||
"abi".to_string(),
|
||||
"evm.bytecode".to_string(),
|
||||
"evm.deployedBytecode".to_string(),
|
||||
"evm.methodIdentifiers".to_string(),
|
||||
],
|
||||
)]),
|
||||
)])
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
|
@ -215,9 +247,9 @@ impl Settings {
|
|||
pub fn push_contract_output_selection(
|
||||
&mut self,
|
||||
contracts: impl Into<String>,
|
||||
value: impl Into<String>,
|
||||
value: impl ToString,
|
||||
) {
|
||||
let value = value.into();
|
||||
let value = value.to_string();
|
||||
let values = self
|
||||
.output_selection
|
||||
.entry("*".to_string())
|
||||
|
@ -230,7 +262,7 @@ impl Settings {
|
|||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
|
@ -240,12 +272,12 @@ impl Settings {
|
|||
pub fn set_contract_output_selection(
|
||||
&mut self,
|
||||
key: impl Into<String>,
|
||||
values: impl IntoIterator<Item = impl Into<String>>,
|
||||
values: impl IntoIterator<Item = impl ToString>,
|
||||
) {
|
||||
self.output_selection
|
||||
.entry("*".to_string())
|
||||
.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
|
||||
|
@ -792,7 +824,11 @@ pub struct Contract {
|
|||
/// The Ethereum Contract Metadata.
|
||||
/// See https://docs.soliditylang.org/en/develop/metadata.html
|
||||
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>,
|
||||
#[serde(default)]
|
||||
pub userdoc: UserDoc,
|
||||
|
@ -1488,7 +1524,7 @@ impl Bytecode {
|
|||
#[serde(untagged)]
|
||||
pub enum BytecodeObject {
|
||||
/// Fully linked bytecode object
|
||||
#[serde(deserialize_with = "deserialize_bytes")]
|
||||
#[serde(deserialize_with = "serde_helpers::deserialize_bytes")]
|
||||
Bytecode(Bytes),
|
||||
/// Bytecode as hex string that's not fully linked yet and contains library placeholders
|
||||
Unlinked(String),
|
||||
|
@ -1738,7 +1774,7 @@ pub struct Ewasm {
|
|||
#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct StorageLayout {
|
||||
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>,
|
||||
}
|
||||
|
||||
|
@ -1778,7 +1814,7 @@ pub struct Error {
|
|||
pub r#type: String,
|
||||
pub component: String,
|
||||
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 message: 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)]
|
||||
mod tests {
|
||||
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