/
githubmirror
/
sway
Обзор
Документация
Войти
/
githubmirror
/
sway
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
forc/src/cli/commands/plugins.rs
147 строк
5 KB
César D. Rodas
Improve cli_examples macro (#5589)
10 фев 2024, 06:54
Не верифицирован
10 фев 2024, 06:54
7bcac37
Код
Авторство
О чём код?
use crate::cli::PluginsCommand; use anyhow::anyhow; use clap::Parser; use forc_tracing::println_warning; use forc_util::ForcResult; use std::{ collections::HashMap, path::{Path, PathBuf}, }; use tracing::info; forc_util::cli_examples! { crate::cli::Opt { [ List all plugins => "forc plugins" ] [ List all plugins with their paths => "forc plugins --paths" ] [ List all plugins with their descriptions => "forc plugins --describe" ] [ List all plugins with their paths and descriptions => "forc plugins --paths --describe" ] } } /// Find all forc plugins available via `PATH`. /// /// Prints information about each discovered plugin. #[derive(Debug, Parser)] #[clap(name = "forc plugins", about = "List all forc plugins", version, after_help = help())] pub struct Command { /// Prints the absolute path to each discovered plugin. #[clap(long = "paths", short = 'p')] print_full_path: bool, /// Prints the long description associated with each listed plugin #[clap(long = "describe", short = 'd')] describe: bool, } fn get_file_name(path: &Path) -> String { if let Some(path_str) = path.file_name().and_then(|path_str| path_str.to_str()) { path_str.to_owned() } else { path.display().to_string() } } pub(crate) fn exec(command: PluginsCommand) -> ForcResult<()> { let PluginsCommand { print_full_path, describe, } = command; let mut plugins = crate::cli::plugin::find_all() .map(|path| { get_plugin_info(path.clone(), print_full_path, describe).map(|info| (path, info)) }) .collect::<Result<Vec<(_, _)>, _>>()? .into_iter() .fold(HashMap::new(), |mut acc, (path, content)| { let bin_name = get_file_name(&path); acc.entry(bin_name.clone()) .or_insert_with(|| (bin_name, vec![], content.clone())) .1 .push(path); acc }) .into_values() .map(|(bin_name, mut paths, content)| { paths.sort(); paths.dedup(); (bin_name, paths, content) }) .collect::<Vec<_>>(); plugins.sort_by(|a, b| a.0.cmp(&b.0)); info!("Installed Plugins:"); for plugin in plugins { info!("{}", plugin.2); if plugin.1.len() > 1 { println_warning(&format!("Multiple paths found for {}", plugin.0)); for path in plugin.1 { println_warning(&format!(" {}", path.display())); } } } Ok(()) } /// Find a plugin's description /// /// Given a canonical plugin path, returns the description included in the `-h` opt. /// Returns a generic description if a description cannot be found fn parse_description_for_plugin(plugin: &Path) -> String { use std::process::Command; let default_description = "No description found for this plugin."; let proc = Command::new(plugin) .arg("-h") .output() .expect("Could not get plugin description."); let stdout = String::from_utf8_lossy(&proc.stdout); // If the plugin doesn't support a -h flag match stdout.split('\n').nth(1) { Some(x) => { if x.is_empty() { default_description.to_owned() } else { x.to_owned() } } None => default_description.to_owned(), } } /// # Panics /// /// Format a given plugin's line to stdout /// /// Formatting is based on a combination of `print_full_path` and `describe`. Will /// panic if there is a problem retrieving a plugin's name or path fn format_print_description( path: PathBuf, print_full_path: bool, describe: bool, ) -> ForcResult<String> { let display = if print_full_path { path.display().to_string() } else { get_file_name(&path) }; let description = parse_description_for_plugin(&path); if describe { Ok(format!(" {display} \t\t{description}")) } else { Ok(display) } } /// # Panics /// /// This function assumes that file names will never be empty since it is only used with /// paths yielded from plugin::find_all(), as well as that the file names are in valid /// unicode format since file names should be prefixed with `forc-`. Should one of these 2 /// assumptions fail, this function panics. fn get_plugin_info(path: PathBuf, print_full_path: bool, describe: bool) -> ForcResult<String> { format_print_description(path, print_full_path, describe) .map_err(|e| anyhow!("Could not get plugin info: {}", e.as_ref()).into()) }