feat(core): implement 'match list' command. Fix #786

This commit is contained in:
Federico Terzi 2021-10-30 20:46:25 +02:00
parent 382f708a02
commit 119d537fb7
4 changed files with 206 additions and 33 deletions

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<dyn ConfigStore>,
match_store: Box<dyn MatchStore>,
) -> 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<String>,
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(())
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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
}

View File

@ -28,6 +28,7 @@ pub mod edit;
pub mod env_path; pub mod env_path;
pub mod launcher; pub mod launcher;
pub mod log; pub mod log;
pub mod match_cli;
pub mod migrate; pub mod migrate;
pub mod modulo; pub mod modulo;
pub mod package; pub mod package;

View File

@ -71,6 +71,7 @@ lazy_static! {
cli::service::new(), cli::service::new(),
cli::workaround::new(), cli::workaround::new(),
cli::package::new(), cli::package::new(),
cli::match_cli::new(),
]; ];
static ref ALIASES: Vec<CliAlias> = vec![ static ref ALIASES: Vec<CliAlias> = vec![
CliAlias { CliAlias {
@ -353,39 +354,57 @@ For example, specifying 'email' is equivalent to 'match/email.yml'."#))
.subcommand(restart_subcommand) .subcommand(restart_subcommand)
.subcommand(stop_subcommand) .subcommand(stop_subcommand)
.subcommand(status_subcommand) .subcommand(status_subcommand)
// .subcommand(SubCommand::with_name("match") .subcommand(SubCommand::with_name("match")
// .about("List and execute matches from the CLI") .about("List and execute matches from the CLI")
// .subcommand(SubCommand::with_name("list") .subcommand(SubCommand::with_name("list")
// .about("Print all matches to standard output") .about("Print matches to standard output")
// .arg(Arg::with_name("json") .arg(Arg::with_name("json")
// .short("j") .short("j")
// .long("json") .long("json")
// .help("Return the matches as json") .help("Output matches to the JSON format")
// .required(false) .required(false)
// .takes_value(false) .takes_value(false)
// ) )
// .arg(Arg::with_name("onlytriggers") .arg(Arg::with_name("onlytriggers")
// .short("t") .short("t")
// .long("onlytriggers") .long("only-triggers")
// .help("Print only triggers without replacement") .help("Print only triggers without replacement")
// .required(false) .required(false)
// .takes_value(false) .takes_value(false)
// ) )
// .arg(Arg::with_name("preservenewlines") .arg(Arg::with_name("preservenewlines")
// .short("n") .short("n")
// .long("preservenewlines") .long("preserve-newlines")
// .help("Preserve newlines when printing replacements") .help("Preserve newlines when printing replacements. Does nothing when using JSON format.")
// .required(false) .required(false)
// .takes_value(false) .takes_value(false)
// ) )
// ) .arg(Arg::with_name("class")
// .subcommand(SubCommand::with_name("exec") .long("class")
// .about("Triggers the expansion of the given match") .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.")
// .arg(Arg::with_name("trigger") .required(false)
// .help("The trigger of the match to be expanded") .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(
SubCommand::with_name("package") SubCommand::with_name("package")
.about("package-management commands") .about("package-management commands")