use std::str::FromStr;
use std::collections::BTreeMap;
use clap;
use toml::Value;
use serde::{Serialize, Deserialize};
use failure;
use blockchain::Service;
pub use self::builder::NodeBuilder;
pub use self::details::{Run, Finalize, GenerateNodeConfig, GenerateCommonConfig, GenerateTestnet};
pub use self::shared::{AbstractConfig, NodePublicConfig, CommonConfigTemplate, NodePrivateConfig};
pub use self::context_key::ContextKey;
mod shared;
mod builder;
mod details;
mod internal;
mod clap_backend;
#[macro_use]
mod context_key;
pub const DEFAULT_EXONUM_LISTEN_PORT: u16 = 6333;
pub type CommandName = &'static str;
#[derive(Clone, Copy, Debug)]
pub struct NamedArgument {
pub short_name: Option<&'static str>,
pub long_name: &'static str,
pub multiple: bool,
}
#[derive(Clone, Copy, Debug)]
pub enum ArgumentType {
Positional,
Named(NamedArgument),
}
#[derive(Clone, Copy, Debug)]
pub struct Argument {
pub name: &'static str,
pub argument_type: ArgumentType,
pub required: bool,
pub help: &'static str,
}
impl Argument {
pub fn new_named<T>(
name: &'static str,
required: bool,
help: &'static str,
short_name: T,
long_name: &'static str,
multiple: bool,
) -> Argument
where
T: Into<Option<&'static str>>,
{
Argument {
argument_type: ArgumentType::Named(NamedArgument {
short_name: short_name.into(),
long_name,
multiple,
}),
name,
help,
required,
}
}
pub fn new_positional(name: &'static str, required: bool, help: &'static str) -> Argument {
Argument {
argument_type: ArgumentType::Positional,
name,
help,
required,
}
}
}
pub mod keys {
use std::collections::BTreeMap;
use toml;
use node::NodeConfig;
use super::shared::{AbstractConfig, CommonConfigTemplate, NodePublicConfig};
use super::ContextKey;
pub const NODE_CONFIG: ContextKey<NodeConfig> = context_key!("node_config");
pub const CONFIGS: ContextKey<Vec<NodeConfig>> = context_key!("configs");
pub const SERVICES_CONFIG: ContextKey<AbstractConfig> = context_key!("services_config");
pub const COMMON_CONFIG: ContextKey<CommonConfigTemplate> = context_key!("common_config");
pub const SERVICES_PUBLIC_CONFIGS: ContextKey<BTreeMap<String, toml::Value>> =
context_key!("services_public_configs");
pub const SERVICES_SECRET_CONFIGS: ContextKey<BTreeMap<String, toml::Value>> =
context_key!("services_secret_configs");
pub const PUBLIC_CONFIG_LIST: ContextKey<Vec<NodePublicConfig>> =
context_key!("public_config_list");
pub const AUDITOR_MODE: ContextKey<bool> = context_key!("auditor_mode");
}
#[derive(PartialEq, Debug, Clone, Default)]
pub struct Context {
args: BTreeMap<String, String>,
multiple_args: BTreeMap<String, Vec<String>>,
variables: BTreeMap<String, Value>,
}
impl Context {
fn new_from_args(args: &[Argument], matches: &clap::ArgMatches) -> Context {
let mut context = Context::default();
for arg in args {
match arg.argument_type {
ArgumentType::Named(detail) if detail.multiple => {
if let Some(values) = matches.values_of(&arg.name) {
let values: Vec<String> = values.map(|e| e.to_owned()).collect();
if context
.multiple_args
.insert(arg.name.to_owned(), values)
.is_some()
{
panic!("Duplicated argument: {}", arg.name);
}
continue;
}
}
_ => (),
};
if let Some(value) = matches.value_of(&arg.name) {
if context
.args
.insert(arg.name.to_owned(), value.to_string())
.is_some()
{
panic!("Duplicated argument: {}", arg.name);
}
} else if arg.required {
panic!("Required argument is not found: {}", arg.name)
}
}
context
}
pub fn arg<T: FromStr>(&self, key: &str) -> Result<T, failure::Error>
where
failure::Error: From<<T as FromStr>::Err>,
{
match self.args.get(key) {
Some(v) => Ok(v.parse()?),
None => bail!("expected `{}` argument", key),
}
}
pub fn set_arg(&mut self, key: &str, value: String) {
self.args.insert(key.into(), value);
}
pub fn arg_multiple<T: FromStr>(&self, key: &str) -> Result<Vec<T>, failure::Error>
where
failure::Error: From<<T as FromStr>::Err>,
{
match self.multiple_args.get(key) {
Some(values) => values.iter().map(|v| Ok(v.parse()?)).collect(),
None => bail!("expected `{}` argument", key),
}
}
pub fn set_arg_multiple(&mut self, key: &str, values: Vec<String>) {
self.multiple_args.insert(key.into(), values);
}
pub fn get<'de, T: Deserialize<'de>>(&self, key: ContextKey<T>) -> Result<T, failure::Error> {
self.get_raw(key.name())
}
pub fn set<T: Serialize>(&mut self, key: ContextKey<T>, value: T) -> Option<Value> {
self.set_raw(key.name(), value)
}
fn get_raw<'de, T: Deserialize<'de>>(&self, key: &str) -> Result<T, failure::Error> {
match self.variables.get(key) {
Some(v) => Ok(v.clone().try_into()?),
_ => bail!("key `{}` not found", key),
}
}
fn set_raw<T: Serialize>(&mut self, key: &str, value: T) -> Option<Value> {
let value: Value = Value::try_from(value).expect("could not convert value into toml");
self.variables.insert(key.to_owned(), value)
}
}
pub trait CommandExtension {
fn args(&self) -> Vec<Argument>;
fn execute(&self, context: Context) -> Result<Context, failure::Error>;
}
pub trait ServiceFactory: 'static {
#[allow(unused_variables)]
fn command(&mut self, command: CommandName) -> Option<Box<CommandExtension>> {
None
}
fn make_service(&mut self, run_context: &Context) -> Box<Service>;
}