mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-15 20:51:05 +00:00
factor out solc JSON interface crate (#264)
The differential testing framework will make a second consumer. There seems to be no re-usable Rust crate for this. But we already have everything here, just needs a small refactor to make it fully re-usable. - Mostly decouple the solc JSON-input-output interface types from the `solidity` frontend crate - Expose the JSON-input-output interface types in a dedicated crate --------- Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
//! The `solc --standard-json` input language.
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
/// The `solc --standard-json` input language.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Language {
|
||||
/// The Solidity language.
|
||||
Solidity,
|
||||
/// The Yul IR.
|
||||
Yul,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Language {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Solidity => write!(f, "Solidity"),
|
||||
Self::Yul => write!(f, "Yul"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
//! The `solc --standard-json` input.
|
||||
|
||||
pub mod language;
|
||||
pub mod settings;
|
||||
pub mod source;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(all(feature = "parallel", feature = "resolc"))]
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata;
|
||||
use crate::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
|
||||
use crate::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection;
|
||||
#[cfg(feature = "resolc")]
|
||||
use crate::warning::Warning;
|
||||
|
||||
use self::language::Language;
|
||||
use self::settings::Settings;
|
||||
use self::source::Source;
|
||||
|
||||
/// The `solc --standard-json` input.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Input {
|
||||
/// The input language.
|
||||
pub language: Language,
|
||||
/// The input source code files hashmap.
|
||||
pub sources: BTreeMap<String, Source>,
|
||||
/// The compiler settings.
|
||||
pub settings: Settings,
|
||||
/// The suppressed warnings.
|
||||
#[cfg(feature = "resolc")]
|
||||
#[serde(skip_serializing)]
|
||||
pub suppressed_warnings: Option<Vec<Warning>>,
|
||||
}
|
||||
|
||||
impl Input {
|
||||
/// A shortcut constructor from stdin.
|
||||
pub fn try_from_stdin() -> anyhow::Result<Self> {
|
||||
let mut input: Self = serde_json::from_reader(std::io::BufReader::new(std::io::stdin()))?;
|
||||
input
|
||||
.settings
|
||||
.output_selection
|
||||
.get_or_insert_with(SolcStandardJsonInputSettingsSelection::default)
|
||||
.extend_with_required();
|
||||
Ok(input)
|
||||
}
|
||||
|
||||
/// A shortcut constructor from paths.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn try_from_paths(
|
||||
language: Language,
|
||||
evm_version: Option<revive_common::EVMVersion>,
|
||||
paths: &[PathBuf],
|
||||
library_map: Vec<String>,
|
||||
remappings: Option<BTreeSet<String>>,
|
||||
output_selection: SolcStandardJsonInputSettingsSelection,
|
||||
optimizer: SolcStandardJsonInputSettingsOptimizer,
|
||||
metadata: Option<SolcStandardJsonInputSettingsMetadata>,
|
||||
#[cfg(feature = "resolc")] suppressed_warnings: Option<Vec<Warning>>,
|
||||
) -> anyhow::Result<Self> {
|
||||
let mut paths: BTreeSet<PathBuf> = paths.iter().cloned().collect();
|
||||
let libraries = Settings::parse_libraries(library_map)?;
|
||||
for library_file in libraries.keys() {
|
||||
paths.insert(PathBuf::from(library_file));
|
||||
}
|
||||
|
||||
let sources = paths
|
||||
.iter()
|
||||
.map(|path| {
|
||||
let source = Source::try_from(path.as_path()).unwrap_or_else(|error| {
|
||||
panic!("Source code file {path:?} reading error: {error}")
|
||||
});
|
||||
(path.to_string_lossy().to_string(), source)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Self {
|
||||
language,
|
||||
sources,
|
||||
settings: Settings::new(
|
||||
evm_version,
|
||||
libraries,
|
||||
remappings,
|
||||
output_selection,
|
||||
optimizer,
|
||||
metadata,
|
||||
),
|
||||
#[cfg(feature = "resolc")]
|
||||
suppressed_warnings,
|
||||
})
|
||||
}
|
||||
|
||||
/// A shortcut constructor from source code.
|
||||
/// Only for the integration test purposes.
|
||||
#[cfg(feature = "resolc")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn try_from_sources(
|
||||
evm_version: Option<revive_common::EVMVersion>,
|
||||
sources: BTreeMap<String, String>,
|
||||
libraries: BTreeMap<String, BTreeMap<String, String>>,
|
||||
remappings: Option<BTreeSet<String>>,
|
||||
output_selection: SolcStandardJsonInputSettingsSelection,
|
||||
optimizer: SolcStandardJsonInputSettingsOptimizer,
|
||||
metadata: Option<SolcStandardJsonInputSettingsMetadata>,
|
||||
suppressed_warnings: Option<Vec<Warning>>,
|
||||
) -> anyhow::Result<Self> {
|
||||
#[cfg(feature = "parallel")]
|
||||
let iter = sources.into_par_iter(); // Parallel iterator
|
||||
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
let iter = sources.into_iter(); // Sequential iterator
|
||||
let sources = iter
|
||||
.map(|(path, content)| (path, Source::from(content)))
|
||||
.collect();
|
||||
|
||||
Ok(Self {
|
||||
language: Language::Solidity,
|
||||
sources,
|
||||
settings: Settings::new(
|
||||
evm_version,
|
||||
libraries,
|
||||
remappings,
|
||||
output_selection,
|
||||
optimizer,
|
||||
metadata,
|
||||
),
|
||||
suppressed_warnings,
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets the necessary defaults.
|
||||
pub fn normalize(&mut self, version: &semver::Version) {
|
||||
self.settings.normalize(version);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//! The `solc --standard-json` input settings metadata.
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::standard_json::input::settings::metadata_hash::MetadataHash;
|
||||
|
||||
/// The `solc --standard-json` input settings metadata.
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Metadata {
|
||||
/// The bytecode hash mode.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bytecode_hash: Option<MetadataHash>,
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
/// A shortcut constructor.
|
||||
pub fn new(bytecode_hash: MetadataHash) -> Self {
|
||||
Self {
|
||||
bytecode_hash: Some(bytecode_hash),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//! The metadata hash mode.
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
/// The metadata hash mode.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum MetadataHash {
|
||||
/// Do not include bytecode hash.
|
||||
#[serde(rename = "none")]
|
||||
None,
|
||||
/// The default keccak256 hash.
|
||||
#[serde(rename = "keccak256")]
|
||||
Keccak256,
|
||||
}
|
||||
|
||||
impl FromStr for MetadataHash {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(string: &str) -> Result<Self, Self::Err> {
|
||||
match string {
|
||||
"none" => Ok(Self::None),
|
||||
"keccak256" => Ok(Self::Keccak256),
|
||||
_ => anyhow::bail!("Unknown bytecode hash mode: `{}`", string),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//! The `solc --standard-json` input settings.
|
||||
|
||||
pub mod metadata;
|
||||
pub mod metadata_hash;
|
||||
pub mod optimizer;
|
||||
pub mod selection;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use self::metadata::Metadata;
|
||||
use self::optimizer::Optimizer;
|
||||
use self::selection::Selection;
|
||||
|
||||
/// The `solc --standard-json` input settings.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Settings {
|
||||
/// The target EVM version.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub evm_version: Option<revive_common::EVMVersion>,
|
||||
/// The linker library addresses.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub libraries: Option<BTreeMap<String, BTreeMap<String, String>>>,
|
||||
/// The sorted list of remappings.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub remappings: Option<BTreeSet<String>>,
|
||||
/// The output selection filters.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub output_selection: Option<Selection>,
|
||||
/// Whether to compile via IR. Only for testing with solc >=0.8.13.
|
||||
#[serde(
|
||||
rename = "viaIR",
|
||||
skip_serializing_if = "Option::is_none",
|
||||
skip_deserializing
|
||||
)]
|
||||
pub via_ir: Option<bool>,
|
||||
/// The optimizer settings.
|
||||
pub optimizer: Optimizer,
|
||||
/// The metadata settings.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub metadata: Option<Metadata>,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
/// A shortcut constructor.
|
||||
pub fn new(
|
||||
evm_version: Option<revive_common::EVMVersion>,
|
||||
libraries: BTreeMap<String, BTreeMap<String, String>>,
|
||||
remappings: Option<BTreeSet<String>>,
|
||||
output_selection: Selection,
|
||||
optimizer: Optimizer,
|
||||
metadata: Option<Metadata>,
|
||||
) -> Self {
|
||||
Self {
|
||||
evm_version,
|
||||
libraries: Some(libraries),
|
||||
remappings,
|
||||
output_selection: Some(output_selection),
|
||||
optimizer,
|
||||
metadata,
|
||||
via_ir: Some(true),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the necessary defaults.
|
||||
pub fn normalize(&mut self, version: &semver::Version) {
|
||||
self.optimizer.normalize(version);
|
||||
}
|
||||
|
||||
/// Parses the library list and returns their double hashmap with path and name as keys.
|
||||
pub fn parse_libraries(
|
||||
input: Vec<String>,
|
||||
) -> anyhow::Result<BTreeMap<String, BTreeMap<String, String>>> {
|
||||
let mut libraries = BTreeMap::new();
|
||||
for (index, library) in input.into_iter().enumerate() {
|
||||
let mut path_and_address = library.split('=');
|
||||
let path = path_and_address
|
||||
.next()
|
||||
.ok_or_else(|| anyhow::anyhow!("The library #{} path is missing", index))?;
|
||||
let mut file_and_contract = path.split(':');
|
||||
let file = file_and_contract
|
||||
.next()
|
||||
.ok_or_else(|| anyhow::anyhow!("The library `{}` file name is missing", path))?;
|
||||
let contract = file_and_contract.next().ok_or_else(|| {
|
||||
anyhow::anyhow!("The library `{}` contract name is missing", path)
|
||||
})?;
|
||||
let address = path_and_address
|
||||
.next()
|
||||
.ok_or_else(|| anyhow::anyhow!("The library `{}` address is missing", path))?;
|
||||
libraries
|
||||
.entry(file.to_owned())
|
||||
.or_insert_with(BTreeMap::new)
|
||||
.insert(contract.to_owned(), address.to_owned());
|
||||
}
|
||||
Ok(libraries)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//! The `solc --standard-json` input settings optimizer details.
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
/// The `solc --standard-json` input settings optimizer details.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Details {
|
||||
/// Whether the pass is enabled.
|
||||
pub peephole: bool,
|
||||
/// Whether the pass is enabled.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub inliner: Option<bool>,
|
||||
/// Whether the pass is enabled.
|
||||
pub jumpdest_remover: bool,
|
||||
/// Whether the pass is enabled.
|
||||
pub order_literals: bool,
|
||||
/// Whether the pass is enabled.
|
||||
pub deduplicate: bool,
|
||||
/// Whether the pass is enabled.
|
||||
pub cse: bool,
|
||||
/// Whether the pass is enabled.
|
||||
pub constant_optimizer: bool,
|
||||
}
|
||||
|
||||
impl Details {
|
||||
/// A shortcut constructor.
|
||||
pub fn new(
|
||||
peephole: bool,
|
||||
inliner: Option<bool>,
|
||||
jumpdest_remover: bool,
|
||||
order_literals: bool,
|
||||
deduplicate: bool,
|
||||
cse: bool,
|
||||
constant_optimizer: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
peephole,
|
||||
inliner,
|
||||
jumpdest_remover,
|
||||
order_literals,
|
||||
deduplicate,
|
||||
cse,
|
||||
constant_optimizer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a set of disabled optimizations.
|
||||
pub fn disabled(version: &semver::Version) -> Self {
|
||||
let inliner = if version >= &semver::Version::new(0, 8, 5) {
|
||||
Some(false)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self::new(false, inliner, false, false, false, false, false)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//! The `solc --standard-json` input settings optimizer.
|
||||
|
||||
pub mod details;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use self::details::Details;
|
||||
|
||||
/// The `solc --standard-json` input settings optimizer.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Optimizer {
|
||||
/// Whether the optimizer is enabled.
|
||||
pub enabled: bool,
|
||||
/// The optimization mode string.
|
||||
#[serde(skip_serializing)]
|
||||
pub mode: Option<char>,
|
||||
/// The `solc` optimizer details.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub details: Option<Details>,
|
||||
/// Whether to try to recompile with -Oz if the bytecode is too large.
|
||||
#[serde(skip_serializing)]
|
||||
pub fallback_to_optimizing_for_size: Option<bool>,
|
||||
}
|
||||
|
||||
impl Optimizer {
|
||||
/// A shortcut constructor.
|
||||
pub fn new(
|
||||
enabled: bool,
|
||||
mode: Option<char>,
|
||||
version: &semver::Version,
|
||||
fallback_to_optimizing_for_size: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
enabled,
|
||||
mode,
|
||||
details: Some(Details::disabled(version)),
|
||||
fallback_to_optimizing_for_size: Some(fallback_to_optimizing_for_size),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the necessary defaults.
|
||||
pub fn normalize(&mut self, version: &semver::Version) {
|
||||
self.details = if version >= &semver::Version::new(0, 5, 5) {
|
||||
Some(Details::disabled(version))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
//! The `solc --standard-json` expected output selection flag.
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
/// The `solc --standard-json` expected output selection flag.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Flag {
|
||||
/// The ABI JSON.
|
||||
#[serde(rename = "abi")]
|
||||
ABI,
|
||||
/// The metadata.
|
||||
#[serde(rename = "metadata")]
|
||||
Metadata,
|
||||
/// The developer documentation.
|
||||
#[serde(rename = "devdoc")]
|
||||
Devdoc,
|
||||
/// The user documentation.
|
||||
#[serde(rename = "userdoc")]
|
||||
Userdoc,
|
||||
/// The function signature hashes JSON.
|
||||
#[serde(rename = "evm.methodIdentifiers")]
|
||||
MethodIdentifiers,
|
||||
/// The storage layout.
|
||||
#[serde(rename = "storageLayout")]
|
||||
StorageLayout,
|
||||
/// The AST JSON.
|
||||
#[serde(rename = "ast")]
|
||||
AST,
|
||||
/// The Yul IR.
|
||||
#[serde(rename = "irOptimized")]
|
||||
Yul,
|
||||
/// The EVM legacy assembly JSON.
|
||||
#[serde(rename = "evm.legacyAssembly")]
|
||||
EVMLA,
|
||||
#[serde(rename = "evm.bytecode")]
|
||||
EVMBC,
|
||||
#[serde(rename = "evm.deployedBytecode")]
|
||||
EVMDBC,
|
||||
/// The assembly code
|
||||
#[serde(rename = "evm.assembly")]
|
||||
Assembly,
|
||||
/// The Ir
|
||||
#[serde(rename = "ir")]
|
||||
Ir,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Flag {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::ABI => write!(f, "abi"),
|
||||
Self::Metadata => write!(f, "metadata"),
|
||||
Self::Devdoc => write!(f, "devdoc"),
|
||||
Self::Userdoc => write!(f, "userdoc"),
|
||||
Self::MethodIdentifiers => write!(f, "evm.methodIdentifiers"),
|
||||
Self::StorageLayout => write!(f, "storageLayout"),
|
||||
Self::AST => write!(f, "ast"),
|
||||
Self::Yul => write!(f, "irOptimized"),
|
||||
Self::EVMLA => write!(f, "evm.legacyAssembly"),
|
||||
Self::EVMBC => write!(f, "evm.bytecode"),
|
||||
Self::EVMDBC => write!(f, "evm.deployedBytecode"),
|
||||
Self::Assembly => write!(f, "evm.assembly"),
|
||||
Self::Ir => write!(f, "ir"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//! The `solc --standard-json` output file selection.
|
||||
|
||||
pub mod flag;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use self::flag::Flag as SelectionFlag;
|
||||
|
||||
/// The `solc --standard-json` output file selection.
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
pub struct File {
|
||||
/// The per-file output selections.
|
||||
#[serde(rename = "", skip_serializing_if = "Option::is_none")]
|
||||
pub per_file: Option<HashSet<SelectionFlag>>,
|
||||
/// The per-contract output selections.
|
||||
#[serde(rename = "*", skip_serializing_if = "Option::is_none")]
|
||||
pub per_contract: Option<HashSet<SelectionFlag>>,
|
||||
}
|
||||
|
||||
impl File {
|
||||
/// Creates the selection required by our compilation process.
|
||||
pub fn new_required() -> Self {
|
||||
Self {
|
||||
per_file: Some(HashSet::from_iter([SelectionFlag::AST])),
|
||||
per_contract: Some(HashSet::from_iter([
|
||||
SelectionFlag::EVMBC,
|
||||
SelectionFlag::EVMDBC,
|
||||
SelectionFlag::MethodIdentifiers,
|
||||
SelectionFlag::Metadata,
|
||||
SelectionFlag::Yul,
|
||||
])),
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends the user's output selection with flag required by our compilation process.
|
||||
pub fn extend_with_required(&mut self) -> &mut Self {
|
||||
let required = Self::new_required();
|
||||
|
||||
self.per_file
|
||||
.get_or_insert_with(HashSet::default)
|
||||
.extend(required.per_file.unwrap_or_default());
|
||||
self.per_contract
|
||||
.get_or_insert_with(HashSet::default)
|
||||
.extend(required.per_contract.unwrap_or_default());
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
//! The `solc --standard-json` output selection.
|
||||
|
||||
pub mod file;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use self::file::File as FileSelection;
|
||||
|
||||
/// The `solc --standard-json` output selection.
|
||||
#[derive(Debug, Serialize, Deserialize, Default, PartialEq)]
|
||||
pub struct Selection {
|
||||
/// Only the 'all' wildcard is available for robustness reasons.
|
||||
#[serde(rename = "*", skip_serializing_if = "Option::is_none")]
|
||||
all: Option<FileSelection>,
|
||||
|
||||
#[serde(skip_serializing_if = "BTreeMap::is_empty", flatten)]
|
||||
pub files: BTreeMap<String, FileSelection>,
|
||||
}
|
||||
|
||||
impl Selection {
|
||||
/// Creates the selection required by our compilation process.
|
||||
pub fn new_required() -> Self {
|
||||
Self {
|
||||
all: Some(FileSelection::new_required()),
|
||||
files: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends the user's output selection with flag required by our compilation process.
|
||||
pub fn extend_with_required(&mut self) -> &mut Self {
|
||||
self.all
|
||||
.get_or_insert_with(FileSelection::new_required)
|
||||
.extend_with_required();
|
||||
for (_, v) in self.files.iter_mut() {
|
||||
v.extend_with_required();
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::SolcStandardJsonInputSettingsSelectionFile;
|
||||
|
||||
use super::Selection;
|
||||
|
||||
#[test]
|
||||
fn per_file() {
|
||||
let init = Selection {
|
||||
all: None,
|
||||
files: BTreeMap::from([(
|
||||
"Test".to_owned(),
|
||||
SolcStandardJsonInputSettingsSelectionFile::new_required(),
|
||||
)]),
|
||||
};
|
||||
|
||||
let deser = serde_json::to_string(&init)
|
||||
.and_then(|string| serde_json::from_str(&string))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(init, deser)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all() {
|
||||
let init = Selection {
|
||||
all: Some(SolcStandardJsonInputSettingsSelectionFile::new_required()),
|
||||
files: BTreeMap::new(),
|
||||
};
|
||||
|
||||
let deser = serde_json::to_string(&init)
|
||||
.and_then(|string| serde_json::from_str(&string))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(init, deser)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_and_override() {
|
||||
let init = Selection {
|
||||
all: Some(SolcStandardJsonInputSettingsSelectionFile::new_required()),
|
||||
files: BTreeMap::from([(
|
||||
"Test".to_owned(),
|
||||
SolcStandardJsonInputSettingsSelectionFile::new_required(),
|
||||
)]),
|
||||
};
|
||||
|
||||
let deser = serde_json::to_string(&init)
|
||||
.and_then(|string| serde_json::from_str(&string))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(init, deser)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//! The `solc --standard-json` input source.
|
||||
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
/// The `solc --standard-json` input source.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Source {
|
||||
/// The source code file content.
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl From<String> for Source {
|
||||
fn from(content: String) -> Self {
|
||||
Self { content }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Path> for Source {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(path: &Path) -> Result<Self, Self::Error> {
|
||||
let content = if path.to_string_lossy() == "-" {
|
||||
let mut solidity_code = String::with_capacity(16384);
|
||||
std::io::stdin()
|
||||
.read_to_string(&mut solidity_code)
|
||||
.map_err(|error| anyhow::anyhow!("<stdin> reading error: {}", error))?;
|
||||
solidity_code
|
||||
} else {
|
||||
std::fs::read_to_string(path)
|
||||
.map_err(|error| anyhow::anyhow!("File {:?} reading error: {}", path, error))?
|
||||
};
|
||||
|
||||
Ok(Self { content })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user