From 119d537fb7772c9e8d358fb6789152981fdbf7ca Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Sat, 30 Oct 2021 20:46:25 +0200 Subject: [PATCH] feat(core): implement 'match list' command. Fix #786 --- espanso/src/cli/match_cli/list.rs | 102 ++++++++++++++++++++++++++++++ espanso/src/cli/match_cli/mod.rs | 51 +++++++++++++++ espanso/src/cli/mod.rs | 1 + espanso/src/main.rs | 85 +++++++++++++++---------- 4 files changed, 206 insertions(+), 33 deletions(-) create mode 100644 espanso/src/cli/match_cli/list.rs create mode 100644 espanso/src/cli/match_cli/mod.rs diff --git a/espanso/src/cli/match_cli/list.rs b/espanso/src/cli/match_cli/list.rs new file mode 100644 index 0000000..53614de --- /dev/null +++ b/espanso/src/cli/match_cli/list.rs @@ -0,0 +1,102 @@ +/* + * This file is part of espanso. + * + * Copyright (C) 2019-2021 Federico Terzi + * + * espanso is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use anyhow::Result; +use clap::ArgMatches; +use espanso_config::{ + config::{AppProperties, ConfigStore}, + matches::{store::MatchStore, Match, MatchCause}, +}; +use serde::Serialize; + +pub fn list_main( + cli_args: &ArgMatches, + config_store: Box, + match_store: Box, +) -> Result<()> { + let only_triggers = cli_args.is_present("onlytriggers"); + let preserve_newlines = cli_args.is_present("preservenewlines"); + + let class = cli_args.value_of("class"); + let title = cli_args.value_of("title"); + let exec = cli_args.value_of("exec"); + + let config = config_store.active(&AppProperties { title, class, exec }); + let match_set = match_store.query(config.match_paths()); + + if cli_args.is_present("json") { + print_matches_as_json(&match_set.matches)?; + } else { + print_matches_as_plain(&match_set.matches, only_triggers, preserve_newlines) + } + + Ok(()) +} + +pub fn print_matches_as_plain(match_list: &[&Match], only_triggers: bool, preserve_newlines: bool) { + for m in match_list { + let triggers = match &m.cause { + MatchCause::None => vec!["(none)".to_string()], + MatchCause::Trigger(trigger_cause) => trigger_cause.triggers.clone(), + MatchCause::Regex(regex_cause) => vec![regex_cause.regex.clone()], + }; + + for trigger in triggers { + if only_triggers { + println!("{}", trigger); + } else { + let description = m.description(); + + if preserve_newlines { + println!("{} - {}", trigger, description) + } else { + println!("{} - {}", trigger, description.replace('\n', " ")) + } + } + } + } +} + +#[derive(Debug, Serialize)] +struct JsonMatchEntry { + triggers: Vec, + replace: String, +} + +pub fn print_matches_as_json(match_list: &[&Match]) -> Result<()> { + let mut entries = Vec::new(); + for m in match_list { + let triggers = match &m.cause { + MatchCause::None => vec!["(none)".to_string()], + MatchCause::Trigger(trigger_cause) => trigger_cause.triggers.clone(), + MatchCause::Regex(regex_cause) => vec![regex_cause.regex.clone()], + }; + + entries.push(JsonMatchEntry { + triggers, + replace: m.description().to_string(), + }) + } + + let json = serde_json::to_string_pretty(&entries)?; + + println!("{}", json); + + Ok(()) +} diff --git a/espanso/src/cli/match_cli/mod.rs b/espanso/src/cli/match_cli/mod.rs new file mode 100644 index 0000000..aa458b8 --- /dev/null +++ b/espanso/src/cli/match_cli/mod.rs @@ -0,0 +1,51 @@ +/* + * This file is part of espanso. + * + * Copyright (C) 2019-2021 Federico Terzi + * + * espanso is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use super::{CliModule, CliModuleArgs}; + +mod list; + +pub fn new() -> CliModule { + CliModule { + requires_config: true, + subcommand: "match".to_string(), + entry: match_main, + ..Default::default() + } +} + +fn match_main(args: CliModuleArgs) -> i32 { + let cli_args = args.cli_args.expect("missing cli_args"); + let config_store = args.config_store.expect("missing config_store"); + let match_store = args.match_store.expect("missing match_store"); + + if let Some(sub_args) = cli_args.subcommand_matches("list") { + if let Err(err) = list::list_main(sub_args, config_store, match_store) { + eprintln!("unable to list matches: {:?}", err); + return 1; + } + } else if let Some(_sub_args) = cli_args.subcommand_matches("exec") { + todo!(); + } else { + eprintln!("Invalid use, please run 'espanso match --help' to get more information."); + return 1; + } + + 0 +} diff --git a/espanso/src/cli/mod.rs b/espanso/src/cli/mod.rs index 2cdba94..6c3fb99 100644 --- a/espanso/src/cli/mod.rs +++ b/espanso/src/cli/mod.rs @@ -28,6 +28,7 @@ pub mod edit; pub mod env_path; pub mod launcher; pub mod log; +pub mod match_cli; pub mod migrate; pub mod modulo; pub mod package; diff --git a/espanso/src/main.rs b/espanso/src/main.rs index d0897ed..f5c9f0b 100644 --- a/espanso/src/main.rs +++ b/espanso/src/main.rs @@ -71,6 +71,7 @@ lazy_static! { cli::service::new(), cli::workaround::new(), cli::package::new(), + cli::match_cli::new(), ]; static ref ALIASES: Vec = vec![ CliAlias { @@ -353,39 +354,57 @@ For example, specifying 'email' is equivalent to 'match/email.yml'."#)) .subcommand(restart_subcommand) .subcommand(stop_subcommand) .subcommand(status_subcommand) - // .subcommand(SubCommand::with_name("match") - // .about("List and execute matches from the CLI") - // .subcommand(SubCommand::with_name("list") - // .about("Print all matches to standard output") - // .arg(Arg::with_name("json") - // .short("j") - // .long("json") - // .help("Return the matches as json") - // .required(false) - // .takes_value(false) - // ) - // .arg(Arg::with_name("onlytriggers") - // .short("t") - // .long("onlytriggers") - // .help("Print only triggers without replacement") - // .required(false) - // .takes_value(false) - // ) - // .arg(Arg::with_name("preservenewlines") - // .short("n") - // .long("preservenewlines") - // .help("Preserve newlines when printing replacements") - // .required(false) - // .takes_value(false) - // ) - // ) - // .subcommand(SubCommand::with_name("exec") - // .about("Triggers the expansion of the given match") - // .arg(Arg::with_name("trigger") - // .help("The trigger of the match to be expanded") - // ) - // ) - // ) + .subcommand(SubCommand::with_name("match") + .about("List and execute matches from the CLI") + .subcommand(SubCommand::with_name("list") + .about("Print matches to standard output") + .arg(Arg::with_name("json") + .short("j") + .long("json") + .help("Output matches to the JSON format") + .required(false) + .takes_value(false) + ) + .arg(Arg::with_name("onlytriggers") + .short("t") + .long("only-triggers") + .help("Print only triggers without replacement") + .required(false) + .takes_value(false) + ) + .arg(Arg::with_name("preservenewlines") + .short("n") + .long("preserve-newlines") + .help("Preserve newlines when printing replacements. Does nothing when using JSON format.") + .required(false) + .takes_value(false) + ) + .arg(Arg::with_name("class") + .long("class") + .help("Only return matches that would be active with the given class. This is relevant if you want to list matches only active inside an app-specific config.") + .required(false) + .takes_value(true) + ) + .arg(Arg::with_name("title") + .long("title") + .help("Only return matches that would be active with the given title. This is relevant if you want to list matches only active inside an app-specific config.") + .required(false) + .takes_value(true) + ) + .arg(Arg::with_name("exec") + .long("exec") + .help("Only return matches that would be active with the given exec. This is relevant if you want to list matches only active inside an app-specific config.") + .required(false) + .takes_value(true) + ) + ) + // .subcommand(SubCommand::with_name("exec") + // .about("Triggers the expansion of the given match") + // .arg(Arg::with_name("trigger") + // .help("The trigger of the match to be expanded") + // ) + // ) + ) .subcommand( SubCommand::with_name("package") .about("package-management commands")