Adding nice error messages for invalid configs.

This commit is contained in:
Tomasz Drwięga 2016-09-11 11:52:12 +02:00
parent 62de1c3891
commit b7bf10d62d
7 changed files with 271 additions and 154 deletions

View File

@ -93,6 +93,6 @@ jit = false
[misc] [misc]
logging = "own_tx=trace" logging = "own_tx=trace"
log_file = "/var/log/parity.log" log_file = "/var/log/parity.log"
no_color = false color = true

View File

@ -0,0 +1,2 @@
[account
unlock = "0x1"

View File

@ -0,0 +1,4 @@
[account]
unlock = "0x1"
passwd = []

View File

@ -60,4 +60,4 @@ jit = false
[misc] [misc]
logging = "own_tx=trace" logging = "own_tx=trace"
log_file = "/var/log/parity.log" log_file = "/var/log/parity.log"
no_color = false color = true

View File

@ -71,151 +71,151 @@ usage! {
} }
{ {
// -- Operating Options // -- Operating Options
flag_mode: String = "active", or |c: &Config| c.parity.mode.clone(), flag_mode: String = "active", or |c: &Config| otry!(c.parity).mode.clone(),
flag_mode_timeout: u64 = 300u64, or |c: &Config| c.parity.mode_timeout.clone(), flag_mode_timeout: u64 = 300u64, or |c: &Config| otry!(c.parity).mode_timeout.clone(),
flag_mode_alarm: u64 = 3600u64, or |c: &Config| c.parity.mode_alarm.clone(), flag_mode_alarm: u64 = 3600u64, or |c: &Config| otry!(c.parity).mode_alarm.clone(),
flag_chain: String = "homestead", or |c: &Config| c.parity.chain.clone(), flag_chain: String = "homestead", or |c: &Config| otry!(c.parity).chain.clone(),
flag_db_path: String = "$HOME/.parity", or |c: &Config| c.parity.db_path.clone(), flag_db_path: String = "$HOME/.parity", or |c: &Config| otry!(c.parity).db_path.clone(),
flag_keys_path: String = "$HOME/.parity/keys", or |c: &Config| c.parity.keys_path.clone(), flag_keys_path: String = "$HOME/.parity/keys", or |c: &Config| otry!(c.parity).keys_path.clone(),
flag_identity: String = "", or |c: &Config| c.parity.identity.clone(), flag_identity: String = "", or |c: &Config| otry!(c.parity).identity.clone(),
// -- Account Options // -- Account Options
flag_unlock: Option<String> = None, flag_unlock: Option<String> = None,
or |c: &Config| c.account.unlock.clone().map(|vec| Some(vec.join(","))), or |c: &Config| otry!(c.account).unlock.clone().map(|vec| Some(vec.join(","))),
flag_password: Vec<String> = Vec::new(), flag_password: Vec<String> = Vec::new(),
or |c: &Config| c.account.password.clone(), or |c: &Config| otry!(c.account).password.clone(),
flag_keys_iterations: u32 = 10240u32, flag_keys_iterations: u32 = 10240u32,
or |c: &Config| c.account.keys_iterations.clone(), or |c: &Config| otry!(c.account).keys_iterations.clone(),
flag_force_signer: bool = false, flag_force_signer: bool = false,
or |c: &Config| c.signer.force.clone(), or |c: &Config| otry!(c.signer).force.clone(),
flag_no_signer: bool = false, flag_no_signer: bool = false,
or |c: &Config| c.signer.disable.clone(), or |c: &Config| otry!(c.signer).disable.clone(),
flag_signer_port: u16 = 8180u16, flag_signer_port: u16 = 8180u16,
or |c: &Config| c.signer.port.clone(), or |c: &Config| otry!(c.signer).port.clone(),
flag_signer_interface: String = "local", flag_signer_interface: String = "local",
or |c: &Config| c.signer.interface.clone(), or |c: &Config| otry!(c.signer).interface.clone(),
flag_signer_path: String = "$HOME/.parity/signer", flag_signer_path: String = "$HOME/.parity/signer",
or |c: &Config| c.signer.path.clone(), or |c: &Config| otry!(c.signer).path.clone(),
// NOTE [todr] For security reasons don't put this to config files // NOTE [todr] For security reasons don't put this to config files
flag_signer_no_validation: bool = false, or |_| None, flag_signer_no_validation: bool = false, or |_| None,
// -- Networking Options // -- Networking Options
flag_no_network: bool = false, flag_no_network: bool = false,
or |c: &Config| c.network.disable.clone(), or |c: &Config| otry!(c.network).disable.clone(),
flag_port: u16 = 30303u16, flag_port: u16 = 30303u16,
or |c: &Config| c.network.port.clone(), or |c: &Config| otry!(c.network).port.clone(),
flag_min_peers: u16 = 25u16, flag_min_peers: u16 = 25u16,
or |c: &Config| c.network.min_peers.clone(), or |c: &Config| otry!(c.network).min_peers.clone(),
flag_max_peers: u16 = 50u16, flag_max_peers: u16 = 50u16,
or |c: &Config| c.network.max_peers.clone(), or |c: &Config| otry!(c.network).max_peers.clone(),
flag_nat: String = "any", flag_nat: String = "any",
or |c: &Config| c.network.nat.clone(), or |c: &Config| otry!(c.network).nat.clone(),
flag_network_id: Option<String> = None, flag_network_id: Option<String> = None,
or |c: &Config| c.network.id.clone().map(Some), or |c: &Config| otry!(c.network).id.clone().map(Some),
flag_bootnodes: Option<String> = None, flag_bootnodes: Option<String> = None,
or |c: &Config| c.network.bootnodes.clone().map(|vec| Some(vec.join(","))), or |c: &Config| otry!(c.network).bootnodes.clone().map(|vec| Some(vec.join(","))),
flag_no_discovery: bool = false, flag_no_discovery: bool = false,
or |c: &Config| c.network.discovery.clone(), or |c: &Config| otry!(c.network).discovery.map(|d| !d).clone(),
flag_node_key: Option<String> = None, flag_node_key: Option<String> = None,
or |c: &Config| c.network.node_key.clone().map(Some), or |c: &Config| otry!(c.network).node_key.clone().map(Some),
flag_reserved_peers: Option<String> = None, flag_reserved_peers: Option<String> = None,
or |c: &Config| c.network.reserved_peers.clone().map(Some), or |c: &Config| otry!(c.network).reserved_peers.clone().map(Some),
flag_reserved_only: bool = false, flag_reserved_only: bool = false,
or |c: &Config| c.network.reserved_only.clone(), or |c: &Config| otry!(c.network).reserved_only.clone(),
// -- API and Console Options // -- API and Console Options
// RPC // RPC
flag_no_jsonrpc: bool = false, flag_no_jsonrpc: bool = false,
or |c: &Config| c.rpc.disable.clone(), or |c: &Config| otry!(c.rpc).disable.clone(),
flag_jsonrpc_port: u16 = 8545u16, flag_jsonrpc_port: u16 = 8545u16,
or |c: &Config| c.rpc.port.clone(), or |c: &Config| otry!(c.rpc).port.clone(),
flag_jsonrpc_interface: String = "local", flag_jsonrpc_interface: String = "local",
or |c: &Config| c.rpc.interface.clone(), or |c: &Config| otry!(c.rpc).interface.clone(),
flag_jsonrpc_cors: Option<String> = None, flag_jsonrpc_cors: Option<String> = None,
or |c: &Config| c.rpc.cors.clone().map(Some), or |c: &Config| otry!(c.rpc).cors.clone().map(Some),
flag_jsonrpc_apis: String = "web3,eth,net,ethcore,personal,traces,rpc", flag_jsonrpc_apis: String = "web3,eth,net,ethcore,personal,traces,rpc",
or |c: &Config| c.rpc.apis.clone().map(|vec| vec.join(",")), or |c: &Config| otry!(c.rpc).apis.clone().map(|vec| vec.join(",")),
flag_jsonrpc_hosts: String = "none", flag_jsonrpc_hosts: String = "none",
or |c: &Config| c.rpc.hosts.clone().map(|vec| vec.join(",")), or |c: &Config| otry!(c.rpc).hosts.clone().map(|vec| vec.join(",")),
// IPC // IPC
flag_no_ipc: bool = false, flag_no_ipc: bool = false,
or |c: &Config| c.ipc.disable.clone(), or |c: &Config| otry!(c.ipc).disable.clone(),
flag_ipc_path: String = "$HOME/.parity/jsonrpc.ipc", flag_ipc_path: String = "$HOME/.parity/jsonrpc.ipc",
or |c: &Config| c.ipc.path.clone(), or |c: &Config| otry!(c.ipc).path.clone(),
flag_ipc_apis: String = "web3,eth,net,ethcore,personal,traces,rpc", flag_ipc_apis: String = "web3,eth,net,ethcore,personal,traces,rpc",
or |c: &Config| c.ipc.apis.clone().map(|vec| vec.join(",")), or |c: &Config| otry!(c.ipc).apis.clone().map(|vec| vec.join(",")),
// DAPPS // DAPPS
flag_no_dapps: bool = false, flag_no_dapps: bool = false,
or |c: &Config| c.dapps.disable.clone(), or |c: &Config| otry!(c.dapps).disable.clone(),
flag_dapps_port: u16 = 8080u16, flag_dapps_port: u16 = 8080u16,
or |c: &Config| c.dapps.port.clone(), or |c: &Config| otry!(c.dapps).port.clone(),
flag_dapps_interface: String = "local", flag_dapps_interface: String = "local",
or |c: &Config| c.dapps.interface.clone(), or |c: &Config| otry!(c.dapps).interface.clone(),
flag_dapps_hosts: String = "none", flag_dapps_hosts: String = "none",
or |c: &Config| c.dapps.hosts.clone().map(|vec| vec.join(",")), or |c: &Config| otry!(c.dapps).hosts.clone().map(|vec| vec.join(",")),
flag_dapps_path: String = "$HOME/.parity/dapps", flag_dapps_path: String = "$HOME/.parity/dapps",
or |c: &Config| c.dapps.path.clone(), or |c: &Config| otry!(c.dapps).path.clone(),
flag_dapps_user: Option<String> = None, flag_dapps_user: Option<String> = None,
or |c: &Config| c.dapps.user.clone().map(Some), or |c: &Config| otry!(c.dapps).user.clone().map(Some),
flag_dapps_pass: Option<String> = None, flag_dapps_pass: Option<String> = None,
or |c: &Config| c.dapps.pass.clone().map(Some), or |c: &Config| otry!(c.dapps).pass.clone().map(Some),
// -- Sealing/Mining Options // -- Sealing/Mining Options
flag_author: Option<String> = None, flag_author: Option<String> = None,
or |c: &Config| c.mining.author.clone().map(Some), or |c: &Config| otry!(c.mining).author.clone().map(Some),
flag_force_sealing: bool = false, flag_force_sealing: bool = false,
or |c: &Config| c.mining.force_sealing.clone(), or |c: &Config| otry!(c.mining).force_sealing.clone(),
flag_reseal_on_txs: String = "own", flag_reseal_on_txs: String = "own",
or |c: &Config| c.mining.reseal_on_txs.clone(), or |c: &Config| otry!(c.mining).reseal_on_txs.clone(),
flag_reseal_min_period: u64 = 2000u64, flag_reseal_min_period: u64 = 2000u64,
or |c: &Config| c.mining.reseal_min_period.clone(), or |c: &Config| otry!(c.mining).reseal_min_period.clone(),
flag_work_queue_size: usize = 20usize, flag_work_queue_size: usize = 20usize,
or |c: &Config| c.mining.work_queue_size.clone(), or |c: &Config| otry!(c.mining).work_queue_size.clone(),
flag_tx_gas_limit: Option<String> = None, flag_tx_gas_limit: Option<String> = None,
or |c: &Config| c.mining.tx_gas_limit.clone().map(Some), or |c: &Config| otry!(c.mining).tx_gas_limit.clone().map(Some),
flag_relay_set: String = "cheap", flag_relay_set: String = "cheap",
or |c: &Config| c.mining.relay_set.clone(), or |c: &Config| otry!(c.mining).relay_set.clone(),
flag_usd_per_tx: String = "0", flag_usd_per_tx: String = "0",
or |c: &Config| c.mining.usd_per_tx.clone(), or |c: &Config| otry!(c.mining).usd_per_tx.clone(),
flag_usd_per_eth: String = "auto", flag_usd_per_eth: String = "auto",
or |c: &Config| c.mining.usd_per_eth.clone(), or |c: &Config| otry!(c.mining).usd_per_eth.clone(),
flag_price_update_period: String = "hourly", flag_price_update_period: String = "hourly",
or |c: &Config| c.mining.price_update_period.clone(), or |c: &Config| otry!(c.mining).price_update_period.clone(),
flag_gas_floor_target: String = "4700000", flag_gas_floor_target: String = "4700000",
or |c: &Config| c.mining.gas_floor_target.clone(), or |c: &Config| otry!(c.mining).gas_floor_target.clone(),
flag_gas_cap: String = "6283184", flag_gas_cap: String = "6283184",
or |c: &Config| c.mining.gas_cap.clone(), or |c: &Config| otry!(c.mining).gas_cap.clone(),
flag_extra_data: Option<String> = None, flag_extra_data: Option<String> = None,
or |c: &Config| c.mining.extra_data.clone().map(Some), or |c: &Config| otry!(c.mining).extra_data.clone().map(Some),
flag_tx_queue_size: usize = 1024usize, flag_tx_queue_size: usize = 1024usize,
or |c: &Config| c.mining.tx_queue_size.clone(), or |c: &Config| otry!(c.mining).tx_queue_size.clone(),
flag_remove_solved: bool = false, flag_remove_solved: bool = false,
or |c: &Config| c.mining.remove_solved.clone(), or |c: &Config| otry!(c.mining).remove_solved.clone(),
flag_notify_work: Option<String> = None, flag_notify_work: Option<String> = None,
or |c: &Config| c.mining.notify_work.clone().map(|vec| Some(vec.join(","))), or |c: &Config| otry!(c.mining).notify_work.clone().map(|vec| Some(vec.join(","))),
// -- Footprint Options // -- Footprint Options
flag_tracing: String = "auto", flag_tracing: String = "auto",
or |c: &Config| c.footprint.tracing.clone(), or |c: &Config| otry!(c.footprint).tracing.clone(),
flag_pruning: String = "auto", flag_pruning: String = "auto",
or |c: &Config| c.footprint.pruning.clone(), or |c: &Config| otry!(c.footprint).pruning.clone(),
flag_cache_size_db: u32 = 64u32, flag_cache_size_db: u32 = 64u32,
or |c: &Config| c.footprint.cache_size_db.clone(), or |c: &Config| otry!(c.footprint).cache_size_db.clone(),
flag_cache_size_blocks: u32 = 8u32, flag_cache_size_blocks: u32 = 8u32,
or |c: &Config| c.footprint.cache_size_blocks.clone(), or |c: &Config| otry!(c.footprint).cache_size_blocks.clone(),
flag_cache_size_queue: u32 = 50u32, flag_cache_size_queue: u32 = 50u32,
or |c: &Config| c.footprint.cache_size_queue.clone(), or |c: &Config| otry!(c.footprint).cache_size_queue.clone(),
flag_cache_size: Option<u32> = None, flag_cache_size: Option<u32> = None,
or |c: &Config| c.footprint.cache_size.clone().map(Some), or |c: &Config| otry!(c.footprint).cache_size.clone().map(Some),
flag_fast_and_loose: bool = false, flag_fast_and_loose: bool = false,
or |c: &Config| c.footprint.fast_and_loose.clone(), or |c: &Config| otry!(c.footprint).fast_and_loose.clone(),
flag_db_compaction: String = "ssd", flag_db_compaction: String = "ssd",
or |c: &Config| c.footprint.db_compaction.clone(), or |c: &Config| otry!(c.footprint).db_compaction.clone(),
flag_fat_db: bool = false, flag_fat_db: bool = false,
or |c: &Config| c.footprint.fat_db.clone(), or |c: &Config| otry!(c.footprint).fat_db.clone(),
// -- Import/Export Options // -- Import/Export Options
flag_from: String = "1", or |_| None, flag_from: String = "1", or |_| None,
@ -225,38 +225,38 @@ usage! {
// -- Snapshot Optons // -- Snapshot Optons
flag_at: String = "latest", or |_| None, flag_at: String = "latest", or |_| None,
flag_no_periodic_snapshot: bool = false, flag_no_periodic_snapshot: bool = false,
or |c: &Config| c.snapshots.disable_periodic.clone(), or |c: &Config| otry!(c.snapshots).disable_periodic.clone(),
// -- Virtual Machine Options // -- Virtual Machine Options
flag_jitvm: bool = false, flag_jitvm: bool = false,
or |c: &Config| c.vm.jit.clone(), or |c: &Config| otry!(c.vm).jit.clone(),
// -- Miscellaneous Options // -- Miscellaneous Options
flag_config: String = "$HOME/.parity/config.toml", or |_| None, flag_config: String = "$HOME/.parity/config.toml", or |_| None,
flag_logging: Option<String> = None, flag_logging: Option<String> = None,
or |c: &Config| c.misc.logging.clone().map(Some), or |c: &Config| otry!(c.misc).logging.clone().map(Some),
flag_log_file: Option<String> = None, flag_log_file: Option<String> = None,
or |c: &Config| c.misc.log_file.clone().map(Some), or |c: &Config| otry!(c.misc).log_file.clone().map(Some),
flag_no_color: bool = false, flag_no_color: bool = false,
or |c: &Config| c.misc.no_color.clone(), or |c: &Config| otry!(c.misc).color.map(|c| !c).clone(),
} }
} }
#[derive(Default, Debug, PartialEq, RustcDecodable)] #[derive(Default, Debug, PartialEq, RustcDecodable)]
struct Config { struct Config {
parity: Operating, parity: Option<Operating>,
account: Account, account: Option<Account>,
signer: Signer, signer: Option<Signer>,
network: Network, network: Option<Network>,
rpc: Rpc, rpc: Option<Rpc>,
ipc: Ipc, ipc: Option<Ipc>,
dapps: Dapps, dapps: Option<Dapps>,
mining: Mining, mining: Option<Mining>,
footprint: Footprint, footprint: Option<Footprint>,
snapshots: Snapshots, snapshots: Option<Snapshots>,
vm: VM, vm: Option<VM>,
misc: Misc, misc: Option<Misc>,
} }
#[derive(Default, Debug, PartialEq, RustcDecodable)] #[derive(Default, Debug, PartialEq, RustcDecodable)]
@ -376,15 +376,14 @@ struct VM {
struct Misc { struct Misc {
logging: Option<String>, logging: Option<String>,
log_file: Option<String>, log_file: Option<String>,
no_color: Option<bool>, color: Option<bool>,
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{ use super::{
Args, Config, Operating, Account, Signer, Network, Rpc, Ipc, Dapps, Mining, Footprint, Snapshots, VM, Misc Args, ArgsError,
Config, Operating, Account, Signer, Network, Rpc, Ipc, Dapps, Mining, Footprint, Snapshots, VM, Misc
}; };
use toml; use toml;
@ -394,7 +393,7 @@ mod tests {
let mut config = Config::default(); let mut config = Config::default();
let mut operating = Operating::default(); let mut operating = Operating::default();
operating.chain = Some("morden".into()); operating.chain = Some("morden".into());
config.parity = operating; config.parity = Some(operating);
// when // when
let args = Args::parse_with_config(&["parity"], config).unwrap(); let args = Args::parse_with_config(&["parity"], config).unwrap();
@ -409,7 +408,7 @@ mod tests {
let mut config = Config::default(); let mut config = Config::default();
let mut operating = Operating::default(); let mut operating = Operating::default();
operating.chain = Some("morden".into()); operating.chain = Some("morden".into());
config.parity = operating; config.parity = Some(operating);
// when // when
let args = Args::parse_with_config(&["parity", "--chain", "xyz"], config).unwrap(); let args = Args::parse_with_config(&["parity", "--chain", "xyz"], config).unwrap();
@ -451,20 +450,20 @@ mod tests {
flag_mode: "active".into(), flag_mode: "active".into(),
flag_mode_timeout: 300u64, flag_mode_timeout: 300u64,
flag_mode_alarm: 3600u64, flag_mode_alarm: 3600u64,
flag_chain: "homestead".into(), flag_chain: "xyz".into(),
flag_db_path: "$HOME/.parity".into(), flag_db_path: "$HOME/.parity".into(),
flag_keys_path: "$HOME/.parity/keys".into(), flag_keys_path: "$HOME/.parity/keys".into(),
flag_identity: "".into(), flag_identity: "".into(),
// -- Account Options // -- Account Options
flag_unlock: None, flag_unlock: Some("0xdeadbeefcafe0000000000000000000000000000".into()),
flag_password: vec![], flag_password: vec!["~/.safe/password.file".into()],
flag_keys_iterations: 10240u32, flag_keys_iterations: 10240u32,
flag_force_signer: false, flag_force_signer: false,
flag_no_signer: false, flag_no_signer: false,
flag_signer_port: 8180u16, flag_signer_port: 8180u16,
flag_signer_interface: "local".into(), flag_signer_interface: "127.0.0.1".into(),
flag_signer_path: "$HOME/.parity/signer".into(), flag_signer_path: "$HOME/.parity/signer".into(),
flag_signer_no_validation: false, flag_signer_no_validation: false,
@ -474,11 +473,11 @@ mod tests {
flag_min_peers: 25u16, flag_min_peers: 25u16,
flag_max_peers: 50u16, flag_max_peers: 50u16,
flag_nat: "any".into(), flag_nat: "any".into(),
flag_network_id: None, flag_network_id: Some("0x1".into()),
flag_bootnodes: None, flag_bootnodes: Some("".into()),
flag_no_discovery: false, flag_no_discovery: false,
flag_node_key: None, flag_node_key: None,
flag_reserved_peers: None, flag_reserved_peers: Some("./path_to_file".into()),
flag_reserved_only: false, flag_reserved_only: false,
// -- API and Console Options // -- API and Console Options
@ -486,14 +485,14 @@ mod tests {
flag_no_jsonrpc: false, flag_no_jsonrpc: false,
flag_jsonrpc_port: 8545u16, flag_jsonrpc_port: 8545u16,
flag_jsonrpc_interface: "local".into(), flag_jsonrpc_interface: "local".into(),
flag_jsonrpc_cors: None, flag_jsonrpc_cors: Some("null".into()),
flag_jsonrpc_apis: "web3,eth,net,ethcore,personal,traces,rpc".into(), flag_jsonrpc_apis: "web3,eth,net,personal,ethcore,traces,rpc".into(),
flag_jsonrpc_hosts: "none".into(), flag_jsonrpc_hosts: "none".into(),
// IPC // IPC
flag_no_ipc: false, flag_no_ipc: false,
flag_ipc_path: "$HOME/.parity/jsonrpc.ipc".into(), flag_ipc_path: "$HOME/.parity/jsonrpc.ipc".into(),
flag_ipc_apis: "web3,eth,net,ethcore,personal,traces,rpc".into(), flag_ipc_apis: "web3,eth,net,personal,ethcore,traces,rpc".into(),
// DAPPS // DAPPS
flag_no_dapps: false, flag_no_dapps: false,
@ -501,26 +500,26 @@ mod tests {
flag_dapps_interface: "local".into(), flag_dapps_interface: "local".into(),
flag_dapps_hosts: "none".into(), flag_dapps_hosts: "none".into(),
flag_dapps_path: "$HOME/.parity/dapps".into(), flag_dapps_path: "$HOME/.parity/dapps".into(),
flag_dapps_user: None, flag_dapps_user: Some("test_user".into()),
flag_dapps_pass: None, flag_dapps_pass: Some("test_pass".into()),
// -- Sealing/Mining Options // -- Sealing/Mining Options
flag_author: None, flag_author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
flag_force_sealing: false, flag_force_sealing: true,
flag_reseal_on_txs: "own".into(), flag_reseal_on_txs: "all".into(),
flag_reseal_min_period: 2000u64, flag_reseal_min_period: 4000u64,
flag_work_queue_size: 20usize, flag_work_queue_size: 20usize,
flag_tx_gas_limit: None, flag_tx_gas_limit: Some("6283184".into()),
flag_relay_set: "cheap".into(), flag_relay_set: "cheap".into(),
flag_usd_per_tx: "0".into(), flag_usd_per_tx: "0".into(),
flag_usd_per_eth: "auto".into(), flag_usd_per_eth: "auto".into(),
flag_price_update_period: "hourly".into(), flag_price_update_period: "hourly".into(),
flag_gas_floor_target: "4700000".into(), flag_gas_floor_target: "4700000".into(),
flag_gas_cap: "6283184".into(), flag_gas_cap: "6283184".into(),
flag_extra_data: None, flag_extra_data: Some("Parity".into()),
flag_tx_queue_size: 1024usize, flag_tx_queue_size: 1024usize,
flag_remove_solved: false, flag_remove_solved: false,
flag_notify_work: None, flag_notify_work: Some("http://localhost:3001".into()),
// -- Footprint Options // -- Footprint Options
flag_tracing: "auto".into(), flag_tracing: "auto".into(),
@ -528,7 +527,7 @@ mod tests {
flag_cache_size_db: 64u32, flag_cache_size_db: 64u32,
flag_cache_size_blocks: 8u32, flag_cache_size_blocks: 8u32,
flag_cache_size_queue: 50u32, flag_cache_size_queue: 50u32,
flag_cache_size: None, flag_cache_size: Some(128),
flag_fast_and_loose: false, flag_fast_and_loose: false,
flag_db_compaction: "ssd".into(), flag_db_compaction: "ssd".into(),
flag_fat_db: false, flag_fat_db: false,
@ -575,18 +574,31 @@ mod tests {
// -- Miscellaneous Options // -- Miscellaneous Options
flag_version: false, flag_version: false,
flag_config: "$HOME/.parity/config.toml".into(), flag_config: "$HOME/.parity/config.toml".into(),
flag_logging: None, flag_logging: Some("own_tx=trace".into()),
flag_log_file: None, flag_log_file: Some("/var/log/parity.log".into()),
flag_no_color: false, flag_no_color: false,
}); });
} }
#[test]
fn should_parse_config_and_return_errors() {
let config1 = Args::parse_config(include_str!("./config.invalid1.toml"));
let config2 = Args::parse_config(include_str!("./config.invalid2.toml"));
match (config1, config2) {
(Err(ArgsError::Parsing(_)), Err(ArgsError::Decode(_))) => {},
(a, b) => {
assert!(false, "Got invalid error types: {:?}, {:?}", a, b);
}
}
}
#[test] #[test]
fn should_deserialize_toml_file() { fn should_deserialize_toml_file() {
let config: Config = toml::decode_str(include_str!("./config.toml")).unwrap(); let config: Config = toml::decode_str(include_str!("./config.toml")).unwrap();
assert_eq!(config, Config { assert_eq!(config, Config {
parity: Operating { parity: Some(Operating {
mode: Some("dark".into()), mode: Some("dark".into()),
mode_timeout: Some(15u64), mode_timeout: Some(15u64),
mode_alarm: Some(10u64), mode_alarm: Some(10u64),
@ -594,20 +606,20 @@ mod tests {
db_path: None, db_path: None,
keys_path: None, keys_path: None,
identity: None, identity: None,
}, }),
account: Account { account: Some(Account {
unlock: Some(vec!["0x1".into(), "0x2".into(), "0x3".into()]), unlock: Some(vec!["0x1".into(), "0x2".into(), "0x3".into()]),
password: Some(vec!["passwdfile path".into()]), password: Some(vec!["passwdfile path".into()]),
keys_iterations: None, keys_iterations: None,
}, }),
signer: Signer { signer: Some(Signer {
force: None, force: None,
disable: Some(true), disable: Some(true),
port: None, port: None,
interface: None, interface: None,
path: None, path: None,
}, }),
network: Network { network: Some(Network {
disable: Some(false), disable: Some(false),
port: None, port: None,
min_peers: Some(10), min_peers: Some(10),
@ -619,21 +631,21 @@ mod tests {
node_key: None, node_key: None,
reserved_peers: Some("./path/to/reserved_peers".into()), reserved_peers: Some("./path/to/reserved_peers".into()),
reserved_only: Some(true), reserved_only: Some(true),
}, }),
rpc: Rpc { rpc: Some(Rpc {
disable: Some(true), disable: Some(true),
port: Some(8180), port: Some(8180),
interface: None, interface: None,
cors: None, cors: None,
apis: None, apis: None,
hosts: None, hosts: None,
}, }),
ipc: Ipc { ipc: Some(Ipc {
disable: None, disable: None,
path: None, path: None,
apis: Some(vec!["rpc".into(), "eth".into()]), apis: Some(vec!["rpc".into(), "eth".into()]),
}, }),
dapps: Dapps { dapps: Some(Dapps {
disable: None, disable: None,
port: Some(8080), port: Some(8080),
path: None, path: None,
@ -641,8 +653,8 @@ mod tests {
hosts: None, hosts: None,
user: Some("username".into()), user: Some("username".into()),
pass: Some("password".into()) pass: Some("password".into())
}, }),
mining: Mining { mining: Some(Mining {
author: Some("0xdeadbeefcafe0000000000000000000000000001".into()), author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
force_sealing: Some(true), force_sealing: Some(true),
reseal_on_txs: Some("all".into()), reseal_on_txs: Some("all".into()),
@ -659,8 +671,8 @@ mod tests {
extra_data: None, extra_data: None,
remove_solved: None, remove_solved: None,
notify_work: None, notify_work: None,
}, }),
footprint: Footprint { footprint: Some(Footprint {
tracing: Some("on".into()), tracing: Some("on".into()),
pruning: Some("fast".into()), pruning: Some("fast".into()),
fast_and_loose: None, fast_and_loose: None,
@ -670,18 +682,18 @@ mod tests {
cache_size_queue: Some(100), cache_size_queue: Some(100),
db_compaction: Some("ssd".into()), db_compaction: Some("ssd".into()),
fat_db: Some(true), fat_db: Some(true),
}, }),
snapshots: Snapshots { snapshots: Some(Snapshots {
disable_periodic: Some(true), disable_periodic: Some(true),
}, }),
vm: VM { vm: Some(VM {
jit: Some(false), jit: Some(false),
}, }),
misc: Misc { misc: Some(Misc {
logging: Some("own_tx=trace".into()), logging: Some("own_tx=trace".into()),
log_file: Some("/var/log/parity.log".into()), log_file: Some("/var/log/parity.log".into()),
no_color: Some(false), color: Some(true),
} })
}); });
} }
} }

View File

@ -14,6 +14,16 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
macro_rules! otry {
($e: expr) => (
match $e {
Some(ref v) => v,
None => {
return None;
}
}
)
}
macro_rules! usage { macro_rules! usage {
( (
{ {
@ -27,33 +37,81 @@ macro_rules! usage {
)* )*
} }
) => { ) => {
use toml;
use std::{fs, io, process};
use std::io::Read;
use util::version; use util::version;
use docopt::{Docopt, Error as DocoptError}; use docopt::{Docopt, Error as DocoptError};
use helpers::replace_home;
use rustc_serialize;
#[derive(Debug)]
pub enum ArgsError {
Docopt(DocoptError),
Parsing(Vec<toml::ParserError>),
Decode(toml::DecodeError),
Config(String, io::Error),
}
impl ArgsError {
pub fn exit(self) -> ! {
match self {
ArgsError::Docopt(e) => e.exit(),
ArgsError::Parsing(errors) => {
println!("There is an error in config file.");
for e in &errors {
println!("{}", e);
}
process::exit(2)
},
ArgsError::Decode(e) => {
println!("You might have supplied invalid parameters in config file.");
println!("{}", e);
process::exit(2)
},
ArgsError::Config(path, e) => {
println!("There was an error reading your config file at: {}", path);
println!("{}", e);
process::exit(2)
}
}
}
}
impl From<DocoptError> for ArgsError {
fn from(e: DocoptError) -> Self { ArgsError::Docopt(e) }
}
impl From<toml::DecodeError> for ArgsError {
fn from(e: toml::DecodeError) -> Self { ArgsError::Decode(e) }
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Args { pub struct Args {
$(
pub $field: $typ,
)*
$( $(
pub $field_a: $typ_a, pub $field_a: $typ_a,
)* )*
$(
pub $field: $typ,
)*
} }
impl Default for Args { impl Default for Args {
fn default() -> Self { fn default() -> Self {
Args { Args {
$(
$field: $default.into(),
)*
$( $(
$field_a: Default::default(), $field_a: Default::default(),
)* )*
$(
$field: $default.into(),
)*
} }
} }
} }
#[derive(Default, Debug, PartialEq, RustcDecodable)] #[derive(Default, Debug, PartialEq, Clone, RustcDecodable)]
struct RawArgs { struct RawArgs {
$( $(
$field_a: $typ_a, $field_a: $typ_a,
@ -65,14 +123,57 @@ macro_rules! usage {
impl Args { impl Args {
pub fn parse<S: AsRef<str>>(command: &[S]) -> Result<Self, DocoptError> { pub fn parse<S: AsRef<str>>(command: &[S]) -> Result<Self, ArgsError> {
Ok(try!(RawArgs::parse(command)).into_args(Default::default())) let raw_args = try!(RawArgs::parse(command));
let config_file = raw_args.flag_config.clone().unwrap_or_else(|| raw_args.clone().into_args(Config::default()).flag_config);
let config_file = replace_home(&config_file);
let config = match (fs::File::open(&config_file), raw_args.flag_config.is_some()) {
// Load config file
(Ok(mut file), _) => {
println!("Loading config file from {}", &config_file);
let mut config = String::new();
try!(file.read_to_string(&mut config).map_err(|e| ArgsError::Config(config_file, e)));
try!(Self::parse_config(&config))
},
// Don't display error in case default config cannot be loaded.
(Err(_), false) => Config::default(),
// Config set from CLI (fail with error)
(Err(e), true) => {
return Err(ArgsError::Config(config_file, e));
},
};
Ok(raw_args.into_args(config))
} }
fn parse_with_config<S: AsRef<str>>(command: &[S], config: Config) -> Result<Self, DocoptError> { #[cfg(test)]
pub fn parse_without_config<S: AsRef<str>>(command: &[S]) -> Result<Self, ArgsError> {
Self::parse_with_config(command, Config::default())
}
#[cfg(test)]
fn parse_with_config<S: AsRef<str>>(command: &[S], config: Config) -> Result<Self, ArgsError> {
Ok(try!(RawArgs::parse(command)).into_args(config)) Ok(try!(RawArgs::parse(command)).into_args(config))
} }
fn parse_config(config: &str) -> Result<Config, ArgsError> {
let mut value_parser = toml::Parser::new(&config);
match value_parser.parse() {
Some(value) => {
let result = rustc_serialize::Decodable::decode(&mut toml::Decoder::new(toml::Value::Table(value)));
match result {
Ok(config) => Ok(config),
Err(e) => {
return Err(e.into());
}
}
},
None => {
return Err(ArgsError::Parsing(value_parser.errors));
},
}
}
pub fn print_version() -> String { pub fn print_version() -> String {
format!(include_str!("./version.txt"), version()) format!(include_str!("./version.txt"), version())
} }

View File

@ -19,8 +19,7 @@ use std::io::Read;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::path::PathBuf; use std::path::PathBuf;
use std::cmp::max; use std::cmp::max;
use cli::Args; use cli::{Args, ArgsError};
use docopt::Error as DocoptError;
use util::{Hashable, U256, Uint, Bytes, version_data, Secret, Address}; use util::{Hashable, U256, Uint, Bytes, version_data, Secret, Address};
use util::log::Colour; use util::log::Colour;
use ethsync::{NetworkConfiguration, is_valid_node_url}; use ethsync::{NetworkConfiguration, is_valid_node_url};
@ -60,7 +59,7 @@ pub struct Configuration {
} }
impl Configuration { impl Configuration {
pub fn parse<S: AsRef<str>>(command: &[S]) -> Result<Self, DocoptError> { pub fn parse<S: AsRef<str>>(command: &[S]) -> Result<Self, ArgsError> {
let args = try!(Args::parse(command)); let args = try!(Args::parse(command));
let config = Configuration { let config = Configuration {
@ -613,7 +612,6 @@ impl Configuration {
} }
fn signer_enabled(&self) -> bool { fn signer_enabled(&self) -> bool {
println!("Force: {:?} {:?}", self.args.flag_force_signer, self.args.flag_unlock);
if self.args.flag_force_signer { if self.args.flag_force_signer {
return true; return true;
} }
@ -647,7 +645,7 @@ mod tests {
fn parse(args: &[&str]) -> Configuration { fn parse(args: &[&str]) -> Configuration {
Configuration { Configuration {
args: Args::parse(args).unwrap(), args: Args::parse_without_config(args).unwrap(),
} }
} }