From 074eae309c434218cd89fd22ac2f1438181da78e Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Fri, 21 May 2021 22:03:54 +0200 Subject: [PATCH] feat(core): add support for modulo in espanso core --- Cargo.lock | 114 +++++++++++++++++++++++++++++++ Cargo.toml | 1 + espanso/Cargo.toml | 8 +++ espanso/src/cli/mod.rs | 1 + espanso/src/cli/modulo/form.rs | 53 ++++++++++++++ espanso/src/cli/modulo/mod.rs | 57 ++++++++++++++++ espanso/src/cli/modulo/search.rs | 58 ++++++++++++++++ espanso/src/main.rs | 55 +++++++++++++-- 8 files changed, 342 insertions(+), 5 deletions(-) create mode 100644 espanso/src/cli/modulo/form.rs create mode 100644 espanso/src/cli/modulo/mod.rs create mode 100644 espanso/src/cli/modulo/search.rs diff --git a/Cargo.lock b/Cargo.lock index 347a27e..33c6fe8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,11 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "0.7.15" @@ -88,6 +94,33 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bzip2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.10+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17fa3d1ac1ca21c5c4e36a97f3c3eb25084576f6fc47bf0139c1123434216c6c" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "cc" version = "1.0.66" @@ -140,6 +173,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam" version = "0.8.0" @@ -309,6 +351,7 @@ dependencies = [ "espanso-inject", "espanso-ipc", "espanso-match", + "espanso-modulo", "espanso-path", "espanso-render", "espanso-ui", @@ -322,6 +365,7 @@ dependencies = [ "named_pipe", "serde", "serde_json", + "serde_yaml", "simplelog", "thiserror", "winapi", @@ -434,6 +478,22 @@ dependencies = [ "unicase", ] +[[package]] +name = "espanso-modulo" +version = "0.1.0" +dependencies = [ + "anyhow", + "cc", + "lazy_static", + "log", + "regex", + "serde", + "serde_json", + "thiserror", + "winres", + "zip", +] + [[package]] name = "espanso-path" version = "0.1.0" @@ -473,6 +533,18 @@ dependencies = [ "widestring", ] +[[package]] +name = "flate2" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + [[package]] name = "float-cmp" version = "0.8.0" @@ -736,6 +808,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + [[package]] name = "mockall" version = "0.9.1" @@ -1402,6 +1484,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + [[package]] name = "treeline" version = "0.1.0" @@ -1528,6 +1619,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winres" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4fb510bbfe5b8992ff15f77a2e6fe6cf062878f0eda00c0f44963a807ca5dc" +dependencies = [ + "toml", +] + [[package]] name = "winrt" version = "0.4.0" @@ -1579,3 +1679,17 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "zip" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c83dc9b784d252127720168abd71ea82bf8c3d96b17dc565b5e2a02854f2b27" +dependencies = [ + "byteorder", + "bzip2", + "crc32fast", + "flate2", + "thiserror", + "time", +] diff --git a/Cargo.toml b/Cargo.toml index 370ca99..d3a4f85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,4 +12,5 @@ members = [ "espanso-render", "espanso-info", "espanso-path", + "espanso-modulo", ] \ No newline at end of file diff --git a/espanso/Cargo.toml b/espanso/Cargo.toml index 89f246f..d9d203e 100644 --- a/espanso/Cargo.toml +++ b/espanso/Cargo.toml @@ -9,10 +9,16 @@ homepage = "https://github.com/federico-terzi/espanso" edition = "2018" [features] +default = ["modulo"] # TODO: we might want to avoid specifying a default and instead rely on cargo make + # If the wayland feature is enabled, all X11 dependencies will be dropped # and only methods suitable for Wayland will be used wayland = ["espanso-detect/wayland", "espanso-inject/wayland", "espanso-clipboard/wayland", "espanso-info/wayland"] +# Compile modulo and all its dependencies (including wxWidgets). If you don't +# enable it, features like Forms and Search might not be available. +modulo = ["espanso-modulo", "espanso-clipboard/avoid-gdi", "espanso-ui/avoid-gdi"] + [dependencies] espanso-detect = { path = "../espanso-detect" } espanso-ui = { path = "../espanso-ui" } @@ -24,6 +30,7 @@ espanso-info = { path = "../espanso-info" } espanso-render = { path = "../espanso-render" } espanso-path = { path = "../espanso-path" } espanso-ipc = { path = "../espanso-ipc" } +espanso-modulo = { path = "../espanso-modulo", optional = true } maplit = "1.0.2" simplelog = "0.9.0" log = "0.4.14" @@ -40,6 +47,7 @@ markdown = "0.3.0" html2text = "0.2.1" log-panics = "2.0.0" fs2 = "0.4.3" +serde_yaml = "0.8.17" [target.'cfg(windows)'.dependencies] named_pipe = "0.4.1" diff --git a/espanso/src/cli/mod.rs b/espanso/src/cli/mod.rs index 7cd1633..42dd910 100644 --- a/espanso/src/cli/mod.rs +++ b/espanso/src/cli/mod.rs @@ -23,6 +23,7 @@ use espanso_path::Paths; pub mod daemon; pub mod log; +pub mod modulo; pub mod path; pub mod worker; diff --git a/espanso/src/cli/modulo/form.rs b/espanso/src/cli/modulo/form.rs new file mode 100644 index 0000000..998d1ee --- /dev/null +++ b/espanso/src/cli/modulo/form.rs @@ -0,0 +1,53 @@ +/* + * 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 clap::{ArgMatches}; +use espanso_modulo::form::*; + +pub fn form_main(matches: &ArgMatches) -> i32 { + let as_json: bool = matches.is_present("json"); + + let input_file = matches + .value_of("input_file") + .expect("missing input, please specify the -i option"); + let data = if input_file == "-" { + use std::io::Read; + let mut buffer = String::new(); + std::io::stdin() + .read_to_string(&mut buffer) + .expect("unable to obtain input from stdin"); + buffer + } else { + std::fs::read_to_string(input_file).expect("unable to read input file") + }; + + let config: config::FormConfig = if !as_json { + serde_yaml::from_str(&data).expect("unable to parse form configuration") + } else { + serde_json::from_str(&data).expect("unable to parse form configuration") + }; + + let form = generator::generate(config); + let values = show(form); + + let output = serde_json::to_string(&values).expect("unable to encode values as JSON"); + println!("{}", output); + + 0 +} diff --git a/espanso/src/cli/modulo/mod.rs b/espanso/src/cli/modulo/mod.rs new file mode 100644 index 0000000..c4d0170 --- /dev/null +++ b/espanso/src/cli/modulo/mod.rs @@ -0,0 +1,57 @@ +/* + * 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}; + +#[cfg(feature = "modulo")] +mod form; +#[cfg(feature = "modulo")] +mod search; + +pub fn new() -> CliModule { + #[allow(clippy::needless_update)] + CliModule { + requires_paths: true, + enable_logs: false, + subcommand: "modulo".to_string(), + entry: modulo_main, + ..Default::default() + } +} + +#[cfg(feature = "modulo")] +fn modulo_main(args: CliModuleArgs) -> i32 { + let paths = args.paths.expect("missing paths in modulo main"); + let cli_args = args.cli_args.expect("missing cli_args in modulo main"); + + if let Some(matches) = cli_args.subcommand_matches("form") { + return form::form_main(matches); + } + + if let Some(matches) = cli_args.subcommand_matches("search") { + return search::search_main(matches); + } + + 0 +} + +#[cfg(not(feature = "modulo"))] +fn modulo_main(_: CliModuleArgs) -> i32 { + panic!("this version of espanso was not compiled with 'modulo' support, please obtain a version that does or recompile it with the 'modulo' feature flag"); +} diff --git a/espanso/src/cli/modulo/search.rs b/espanso/src/cli/modulo/search.rs new file mode 100644 index 0000000..5894cb2 --- /dev/null +++ b/espanso/src/cli/modulo/search.rs @@ -0,0 +1,58 @@ +/* + * 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 std::collections::HashMap; +use clap::{ArgMatches}; +use espanso_modulo::search::*; + +pub fn search_main(matches: &ArgMatches) -> i32 { + let as_json: bool = matches.is_present("json"); + + let input_file = matches + .value_of("input_file") + .expect("missing input, please specify the -i option"); + let data = if input_file == "-" { + use std::io::Read; + let mut buffer = String::new(); + std::io::stdin() + .read_to_string(&mut buffer) + .expect("unable to obtain input from stdin"); + buffer + } else { + std::fs::read_to_string(input_file).expect("unable to read input file") + }; + + let config: config::SearchConfig = if !as_json { + serde_yaml::from_str(&data).expect("unable to parse search configuration") + } else { + serde_json::from_str(&data).expect("unable to parse search configuration") + }; + + let algorithm = algorithm::get_algorithm(&config.algorithm); + + let search = generator::generate(config); + let result = show(search, algorithm); + let mut result_map = HashMap::new(); + result_map.insert("selected", result); + + let output = serde_json::to_string(&result_map).expect("unable to encode values as JSON"); + println!("{}", output); + + 0 +} diff --git a/espanso/src/main.rs b/espanso/src/main.rs index 9f009da..3ae99ca 100644 --- a/espanso/src/main.rs +++ b/espanso/src/main.rs @@ -52,7 +52,8 @@ lazy_static! { cli::path::new(), cli::log::new(), cli::worker::new(), - cli::daemon::new() + cli::daemon::new(), + cli::modulo::new(), ]; } @@ -140,14 +141,54 @@ fn main() { // ) // .subcommand(SubCommand::with_name("detect") // .about("Tool to detect current window properties, to simplify filters creation.")) - .subcommand(SubCommand::with_name("daemon") - .setting(AppSettings::Hidden) - .about("Start the daemon without spawning a new process.")) + .subcommand( + SubCommand::with_name("daemon") + .setting(AppSettings::Hidden) + .about("Start the daemon without spawning a new process."), + ) // .subcommand(SubCommand::with_name("register") // .about("MacOS and Linux only. Register espanso in the system daemon manager.")) // .subcommand(SubCommand::with_name("unregister") // .about("MacOS and Linux only. Unregister espanso from the system daemon manager.")) .subcommand(SubCommand::with_name("log").about("Print the daemon logs.")) + .subcommand( + SubCommand::with_name("modulo") + .setting(AppSettings::Hidden) + .subcommand( + SubCommand::with_name("form") + .about("Display a customizable form") + .arg( + Arg::with_name("input_file") + .short("i") + .takes_value(true) + .help("Input file or - for stdin"), + ) + .arg( + Arg::with_name("json") + .short("j") + .required(false) + .takes_value(false) + .help("Interpret the input data as JSON"), + ), + ) + .subcommand( + SubCommand::with_name("search") + .about("Display a search box") + .arg( + Arg::with_name("input_file") + .short("i") + .takes_value(true) + .help("Input file or - for stdin"), + ) + .arg( + Arg::with_name("json") + .short("j") + .required(false) + .takes_value(false) + .help("Interpret the input data as JSON"), + ), + ), + ) // .subcommand(SubCommand::with_name("start") // .about("Start the daemon spawning a new process in the background.")) // .subcommand(SubCommand::with_name("stop") @@ -259,7 +300,11 @@ fn main() { if handler.enable_logs { let config = ConfigBuilder::new() .set_time_to_local(true) - .set_time_format(format!("%H:%M:%S [{}({})]", handler.subcommand, std::process::id())) + .set_time_format(format!( + "%H:%M:%S [{}({})]", + handler.subcommand, + std::process::id() + )) .set_location_level(LevelFilter::Off) .add_filter_ignore_str("html5ever") .build();