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();