ckb-cli 1.4.0

ckb command line interface
use clap::{App, Arg, ArgMatches};
use std::path::PathBuf;

use super::{CliSubCommand, Output};
use crate::plugin::PluginManager;
use crate::utils::arg_parser::{ArgParser, FilePathParser};

pub struct PluginSubCommand<'a> {
    plugin_mgr: &'a mut PluginManager,
}

impl<'a> PluginSubCommand<'a> {
    pub fn new(plugin_mgr: &'a mut PluginManager) -> PluginSubCommand {
        PluginSubCommand { plugin_mgr }
    }

    pub fn subcommand(name: &'static str) -> App<'static> {
        let arg_plugin_name = Arg::with_name("name")
            .long("name")
            .required(true)
            .takes_value(true)
            .about("Plugin name");
        App::new(name)
            .about("ckb-cli plugin management")
            .subcommands(vec![
                App::new("active")
                    .about(
                        "Active a plugin (at most one keystore/indexer role plugin can be actived)",
                    )
                    .arg(arg_plugin_name.clone()),
                App::new("deactive")
                    .about("Deactive a plugin")
                    .arg(arg_plugin_name.clone()),
                App::new("list").about("List all plugins"),
                App::new("info")
                    .about("Show the detail information of a plugin")
                    .arg(arg_plugin_name.clone()),
                App::new("install")
                    .about("Install a plugin, will active it immediately by default")
                    .arg(
                        Arg::with_name("binary-path")
                            .long("binary-path")
                            .required(true)
                            .takes_value(true)
                            .validator(|input| FilePathParser::new(true).validate(input))
                            .about("The binary file path of the plugin"),
                    )
                    .arg(
                        Arg::with_name("inactive")
                            .long("inactive")
                            .about("Install the plugin but not active it"),
                    ),
                App::new("uninstall")
                    .about("Uninstall a plugin, deactive it then remove the binary file")
                    .arg(arg_plugin_name.clone()),
            ])
    }
}

impl<'a> CliSubCommand for PluginSubCommand<'a> {
    fn process(&mut self, matches: &ArgMatches, _debug: bool) -> Result<Output, String> {
        match matches.subcommand() {
            ("active", Some(m)) => {
                let name = m.value_of("name").unwrap();
                self.plugin_mgr.active(name)?;
                Ok(Output::new_output(serde_json::json!(format!(
                    "Plugin {} is actived!",
                    name
                ))))
            }
            ("deactive", Some(m)) => {
                let name = m.value_of("name").unwrap();
                self.plugin_mgr.deactive(name)?;
                Ok(Output::new_output(serde_json::json!(format!(
                    "Plugin {} is deactived!",
                    name
                ))))
            }
            ("list", Some(_)) => {
                let resp = self
                    .plugin_mgr
                    .plugins()
                    .values()
                    .map(|(plugin, config)| {
                        serde_json::json!({
                            "name": config.name,
                            "description": config.description,
                            "is_active": plugin.is_active(),
                        })
                    })
                    .collect::<Vec<_>>();
                Ok(Output::new_output(resp))
            }
            ("info", Some(m)) => {
                let name = m.value_of("name").unwrap();
                if let Some((plugin, config)) = self.plugin_mgr.plugins().get(name) {
                    let resp = serde_json::json!({
                        "name": config.name,
                        "description": config.description,
                        "daemon": config.daemon,
                        "is_active": plugin.is_active(),
                        "roles": serde_json::json!(config.roles),
                    });
                    Ok(Output::new_output(resp))
                } else {
                    Err(format!("Plugin {} not found", name))
                }
            }
            ("install", Some(m)) => {
                let path: PathBuf = FilePathParser::new(true).from_matches(m, "binary-path")?;
                let active = !m.is_present("inactive");
                let config = self.plugin_mgr.install(path, active)?;
                let resp = serde_json::json!({
                    "name": config.name,
                    "description": config.description,
                    "daemon": config.daemon,
                });
                Ok(Output::new_output(resp))
            }
            ("uninstall", Some(m)) => {
                let name = m.value_of("name").unwrap();
                self.plugin_mgr.uninstall(name)?;
                Ok(Output::new_output(serde_json::json!(format!(
                    "Plugin {} uninstalled!",
                    name
                ))))
            }
            _ => Err(Self::subcommand("plugin").generate_usage()),
        }
    }
}