//! Combines the values we care about from the clap CLI and the modifiers module into something //! serializable by serde. use std::collections::HashMap; use anyhow::Context; use log::error; use serde::Serialize; use crate::modifiers; #[derive(Debug, Serialize)] pub struct Command<'a> { subcommands: Vec>, } impl Command<'_> { pub fn new(command: &clap::Command) -> Command<'_> { let subcommand_map: HashMap<&str, &clap::Command> = command .get_subcommands() .map(|s| (s.get_name(), s)) .collect(); Command { // this resulting vector contains the subcommands that are found in both // command.get_subcommands() and modifiers::subcommand_modifiers() in the order defined // by subcommand_modifiers() subcommands: modifiers::subcommand_modifiers() .iter() .filter_map(|modifier| match subcommand_map.get(modifier.command) { Some(clap_command) => Some(Subcommand::new(clap_command, modifier)), None => { error!( "failed to find clap command for subcommand {}", modifier.command ); None } }) .collect(), } } } #[derive(Debug, Serialize)] struct Argument<'a> { advanced: bool, flag: String, label: &'a str, takes_values: bool, } #[derive(Debug, Serialize)] struct Subcommand<'a> { arguments: Vec>, command: &'a str, label: &'a str, } impl Argument<'_> { fn try_new<'a>( argument: &'a clap::Arg, modifier: &modifiers::ArgumentModifier<'static>, ) -> anyhow::Result> { let partial_flag = argument.get_long().with_context(|| { format!( "Missing long form command line flag for {}", argument.get_id().as_str(), ) })?; Ok(Argument { advanced: modifier.advanced, flag: format!("--{}", partial_flag), label: modifier.gui_label, takes_values: argument.get_action().takes_values(), }) } } impl Subcommand<'_> { fn new<'a>( command: &'a clap::Command, modifier: &modifiers::SubcommandModifier<'static>, ) -> Subcommand<'a> { let argument_map: HashMap<&str, &clap::Arg> = command .get_arguments() .map(|a| (a.get_id().as_str(), a)) .collect(); Subcommand { // this resulting vector contains the arguments that are found in both // command.get_arguments() and modifier.arg_modifiers in the order defined by by // arg_modifiers arguments: modifier .arg_modifiers .iter() .filter_map(|arg_modifier| { let Some(arg) = argument_map.get(arg_modifier.clap_id) else { error!( "failed to find clap argument with id {}", arg_modifier.clap_id ); return None; }; match Argument::try_new(arg, arg_modifier) { Ok(modified_arg) => Some(modified_arg), Err(err) => { error!("failed to create modified argument: {:?}", err); None } } }) .collect(), command: modifier.command, label: modifier.gui_label, } } }