Add form extension

This commit is contained in:
Federico Terzi 2020-08-09 11:48:23 +02:00
parent a6b78e7142
commit 45f90c87ed
5 changed files with 92 additions and 11 deletions

78
src/extension/form.rs Normal file
View File

@ -0,0 +1,78 @@
/*
* This file is part of espanso.
*
* Copyright (C) 2020 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 serde_yaml::{Mapping, Value};
use std::collections::HashMap;
use crate::{ui::modulo::ModuloManager, extension::ExtensionResult, config::Configs};
use log::error;
pub struct FormExtension {
manager: ModuloManager,
}
impl FormExtension {
pub fn new(config: &Configs) -> FormExtension {
let manager = ModuloManager::new(config);
FormExtension {
manager,
}
}
}
impl super::Extension for FormExtension {
fn name(&self) -> String {
"form".to_owned()
}
fn calculate(&self, params: &Mapping, _: &Vec<String>, _: &HashMap<String, ExtensionResult>) -> Option<ExtensionResult> {
let layout = params.get(&Value::from("layout"));
let layout = if let Some(value) = layout {
value.as_str().unwrap_or_default().to_string()
} else {
error!("invoking form extension without specifying a layout");
return None;
};
let mut form_config = Mapping::new();
form_config.insert(Value::from("layout"), Value::from(layout));
if let Some(fields) = params.get(&Value::from("fields")) {
form_config.insert(Value::from("fields"), fields.clone());
}
let serialized_config: String = serde_yaml::to_string(&form_config).expect("unable to serialize form config");
let output = self.manager.invoke(&["form", "-i", "-"], &serialized_config);
if let Some(output) = output {
let json: Result<HashMap<String, String>, _> = serde_json::from_str(&output);
match json {
Ok(json) => {
return Some(ExtensionResult::Multiple(json));
}
Err(error) => {
error!("modulo json parsing error: {}", error);
return None;
}
}
} else {
error!("modulo form didn't return any output");
return None;
}
}
}

View File

@ -17,7 +17,7 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::clipboard::ClipboardManager; use crate::{config::Configs, clipboard::ClipboardManager};
use serde_yaml::Mapping; use serde_yaml::Mapping;
use std::collections::HashMap; use std::collections::HashMap;
@ -30,6 +30,7 @@ mod shell;
pub mod multiecho; pub mod multiecho;
pub mod vardummy; pub mod vardummy;
mod utils; mod utils;
mod form;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum ExtensionResult { pub enum ExtensionResult {
@ -42,7 +43,7 @@ pub trait Extension {
fn calculate(&self, params: &Mapping, args: &Vec<String>, current_vars: &HashMap<String, ExtensionResult>) -> Option<ExtensionResult>; fn calculate(&self, params: &Mapping, args: &Vec<String>, current_vars: &HashMap<String, ExtensionResult>) -> Option<ExtensionResult>;
} }
pub fn get_extensions(clipboard_manager: Box<dyn ClipboardManager>) -> Vec<Box<dyn Extension>> { pub fn get_extensions(config: &Configs, clipboard_manager: Box<dyn ClipboardManager>) -> Vec<Box<dyn Extension>> {
vec![ vec![
Box::new(date::DateExtension::new()), Box::new(date::DateExtension::new()),
Box::new(shell::ShellExtension::new()), Box::new(shell::ShellExtension::new()),
@ -52,5 +53,6 @@ pub fn get_extensions(clipboard_manager: Box<dyn ClipboardManager>) -> Vec<Box<d
Box::new(dummy::DummyExtension::new("dummy")), Box::new(dummy::DummyExtension::new("dummy")),
Box::new(dummy::DummyExtension::new("echo")), Box::new(dummy::DummyExtension::new("echo")),
Box::new(clipboard::ClipboardExtension::new(clipboard_manager)), Box::new(clipboard::ClipboardExtension::new(clipboard_manager)),
Box::new(form::FormExtension::new(config)),
] ]
} }

View File

@ -671,7 +671,7 @@ fn worker_background(
let keyboard_manager = keyboard::get_manager(); let keyboard_manager = keyboard::get_manager();
let extensions = extension::get_extensions(Box::new(clipboard::get_manager())); let extensions = extension::get_extensions(config_manager.default_config(), Box::new(clipboard::get_manager()));
let renderer = let renderer =
render::default::DefaultRenderer::new(extensions, config_manager.default_config().clone()); render::default::DefaultRenderer::new(extensions, config_manager.default_config().clone());

View File

@ -119,8 +119,6 @@ impl super::Renderer for DefaultRenderer {
// Then the ones explicitly specified, in the given order // Then the ones explicitly specified, in the given order
variables.extend(&content.vars); variables.extend(&content.vars);
println!("{:?}", variables);
// Replace variable type "global" with the actual reference // Replace variable type "global" with the actual reference
let variables: Vec<&MatchVariable> = variables.into_iter().map(|variable| { let variables: Vec<&MatchVariable> = variables.into_iter().map(|variable| {
if variable.var_type == "global" { if variable.var_type == "global" {
@ -131,8 +129,6 @@ impl super::Renderer for DefaultRenderer {
variable variable
}).collect(); }).collect();
println!("{:?}", variables);
let mut output_map: HashMap<String, ExtensionResult> = HashMap::new(); let mut output_map: HashMap<String, ExtensionResult> = HashMap::new();
for variable in variables.into_iter() { for variable in variables.into_iter() {

View File

@ -12,10 +12,13 @@ pub struct ModuloManager {
impl ModuloManager { impl ModuloManager {
pub fn new(config: &Configs) -> Self { pub fn new(config: &Configs) -> Self {
let mut modulo_path: Option<String> = None; let mut modulo_path: Option<String> = None;
if let Some(ref _modulo_path) = config.modulo_path { // Check if the `MODULO_PATH` env variable is configured
if let Some(_modulo_path) = std::env::var_os("MODULO_PATH") {
modulo_path = Some(_modulo_path.to_string_lossy().to_string())
} else if let Some(ref _modulo_path) = config.modulo_path { // Check the configs
modulo_path = Some(_modulo_path.to_owned()); modulo_path = Some(_modulo_path.to_owned());
}else{ }else{
// First check in the same directory of espanso // Check in the same directory of espanso
if let Ok(exe_path) = std::env::current_exe() { if let Ok(exe_path) = std::env::current_exe() {
if let Some(parent) = exe_path.parent() { if let Some(parent) = exe_path.parent() {
let possible_path = parent.join("modulo"); let possible_path = parent.join("modulo");
@ -59,16 +62,18 @@ impl ModuloManager {
None None
} }
fn invoke(&self, args: &[&str], body: &str) -> Option<String> { pub fn invoke(&self, args: &[&str], body: &str) -> Option<String> {
if self.modulo_path.is_none() { if self.modulo_path.is_none() {
error!("Attempt to invoke modulo even though it's not configured"); error!("Attempt to invoke modulo even though it's not configured");
return None; return None;
} }
if let Some(ref modulo_path) = self.modulo_path { if let Some(ref modulo_path) = self.modulo_path {
let mut child = Command::new(modulo_path) let child = Command::new(modulo_path)
.args(args) .args(args)
.stdin(std::process::Stdio::piped()) .stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn(); .spawn();
match child { match child {