extern crate toml;
extern crate toml_query;
pub mod error;
use crate::blockchain;
use crate::compiler;
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use std::default::Default;
use toml_query::set::TomlValueSetExt;
use toml_query::delete::TomlValueDeleteExt;
use toml_query::insert::TomlValueInsertExt;
use toml_query::error::Error::IdentifierNotFoundInDocument;
use blockchain::connector::BlockchainConnectorConfig;
pub const VIBRANIUM_CONFIG_FILE: &str = "vibranium.toml";
pub const DEFAULT_ARTIFACTS_DIRECTORY: &str = "artifacts";
pub const DEFAULT_CONTRACTS_DIRECTORY: &str = "contracts";
#[derive(Serialize, Deserialize, Debug)]
pub struct ProjectConfig {
pub sources: ProjectSourcesConfig,
pub compiler: Option<ProjectCmdExecutionConfig>,
pub blockchain: Option<ProjectBlockchainConfig>,
pub deployment: Option<ProjectDeploymentConfig>,
}
impl Default for ProjectConfig {
fn default() -> Self {
ProjectConfig {
sources: ProjectSourcesConfig::default(),
compiler: Some(ProjectCmdExecutionConfig::default()),
blockchain: Some(ProjectBlockchainConfig::default()),
deployment: None
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ProjectCmdExecutionConfig {
pub cmd: Option<String>,
pub options: Option<Vec<String>>
}
impl Default for ProjectCmdExecutionConfig {
fn default() -> Self {
ProjectCmdExecutionConfig {
cmd: Some(compiler::support::SupportedCompilers::Solc.to_string()),
options: Some(compiler::support::default_options_from(compiler::support::SupportedCompilers::Solc))
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ProjectBlockchainConfig {
pub cmd: Option<String>,
pub options: Option<Vec<String>>,
pub connector: Option<BlockchainConnectorConfig>,
}
impl Default for ProjectBlockchainConfig {
fn default() -> Self {
ProjectBlockchainConfig {
cmd: Some(blockchain::support::SupportedBlockchainClients::Parity.to_string()),
options: Some(blockchain::support::default_options_from(blockchain::support::SupportedBlockchainClients::Parity)),
connector: Some(blockchain::connector::BlockchainConnectorConfig::default()),
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ProjectSourcesConfig {
pub artifacts: String,
pub smart_contracts: Vec<String>,
}
impl Default for ProjectSourcesConfig {
fn default() -> Self {
ProjectSourcesConfig {
artifacts: DEFAULT_ARTIFACTS_DIRECTORY.to_string(),
smart_contracts: vec![DEFAULT_CONTRACTS_DIRECTORY.to_string() + "/*.sol"],
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ProjectDeploymentConfig {
pub tx_confirmations: Option<usize>,
pub gas_price: Option<usize>,
pub gas_limit: Option<usize>,
pub smart_contracts: Vec<SmartContractConfig>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SmartContractConfig {
pub name: String,
pub args: Option<Vec<SmartContractArg>>,
pub gas_price: Option<usize>,
pub gas_limit: Option<usize>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SmartContractArg {
pub value: String,
pub kind: String,
}
#[derive(Default, Debug)]
pub struct Config {
pub project_path: PathBuf,
pub config_file: PathBuf,
}
impl Config {
pub fn new(path: PathBuf) -> Config {
Config {
project_path: path.clone(),
config_file: path.join(VIBRANIUM_CONFIG_FILE)
}
}
pub fn exists(&self) -> bool {
self.config_file.exists()
}
pub fn read(&self) -> Result<ProjectConfig, error::ConfigError> {
toml::from_str(&fs::read_to_string(&self.config_file).map_err(error::ConfigError::Io)?).map_err(error::ConfigError::Deserialization)
}
pub fn write(&self, option: String, value: toml::Value) -> Result<(), error::ConfigError> {
let mut config = self.try_from_config_file()?;
if let Err(err) = config.set(&option, value.clone()) {
match err {
IdentifierNotFoundInDocument(_message) => {
config.insert(&option, value.clone()).map_err(error::ConfigError::Query)?;
},
_ => Err(error::ConfigError::Query(err))?
}
}
config.try_into::<ProjectConfig>()
.map_err(error::ConfigError::Deserialization)
.and_then(|cfg| {
let config_toml = toml::to_string(&cfg).map_err(error::ConfigError::Serialization)?;
let mut config_file = fs::File::create(&self.config_file)
.map_err(error::ConfigError::Io)?;
config_file.write_all(config_toml.as_bytes()).map_err(error::ConfigError::Io)
})?;
Ok(())
}
pub fn remove(&self, option: String) -> Result<(), error::ConfigError> {
let mut config = self.try_from_config_file()?;
if let Err(err) = config.delete(&option) {
match err {
IdentifierNotFoundInDocument(field) => {
info!("Couldn't delete unsupported option {}", field);
}
_ => Err(error::ConfigError::Deletion(err))?
}
}
self.try_into_config_file(config.clone())?;
Ok(())
}
fn try_from_config_file(&self) -> Result<toml::Value, error::ConfigError> {
toml::Value::try_from(self.read()?).map_err(error::ConfigError::Serialization)
}
fn try_into_config_file(&self, config: toml::Value) -> Result<(), error::ConfigError> {
config.try_into::<ProjectConfig>()
.map_err(error::ConfigError::Deserialization)
.and_then(|cfg| {
let config_toml = toml::to_string(&cfg).map_err(error::ConfigError::Serialization)?;
let mut config_file = fs::File::create(&self.config_file)
.map_err(error::ConfigError::Io)?;
config_file.write_all(config_toml.as_bytes()).map_err(error::ConfigError::Io)
})
}
}