refactor(core): refactor modulo form invocation structure

This commit is contained in:
Federico Terzi 2021-05-07 22:11:11 +02:00
parent 248b62a6a2
commit 6d5507cc5f
9 changed files with 307 additions and 131 deletions

View File

@ -54,7 +54,8 @@ pub fn initialize_and_spawn(
super::engine::matcher::convert::MatchConverter::new(&*config_store, &*match_store);
let match_cache = super::engine::match_cache::MatchCache::load(&*config_store, &*match_store);
let modulo_manager = ui::modulo::ModuloManager::new();
let modulo_manager = crate::gui::modulo::manager::ModuloManager::new();
let modulo_form_ui = crate::gui::modulo::form::ModuloFormUI::new(&modulo_manager, icon_paths.form_icon);
let (detect_source, modifier_state_store, sequencer) =
super::engine::source::init_and_spawn().expect("failed to initialize detector module");
@ -94,8 +95,7 @@ pub fn initialize_and_spawn(
&paths.packages,
);
let shell_extension = espanso_render::extension::shell::ShellExtension::new(&paths.config);
let form_adapter =
ui::modulo::form::ModuloFormProviderAdapter::new(&modulo_manager, icon_paths.form_icon);
let form_adapter = ui::form::FormProviderAdapter::new(&modulo_form_ui);
let form_extension = espanso_render::extension::form::FormExtension::new(&form_adapter);
let renderer = espanso_render::create(vec![
&clipboard_extension,

View File

@ -0,0 +1,117 @@
/*
* 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 std::collections::HashMap;
use espanso_render::{
extension::form::{FormProvider, FormProviderResult},
Params, Value,
};
use log::error;
use crate::gui::{FormField, FormUI};
pub struct FormProviderAdapter<'a> {
form_ui: &'a dyn FormUI,
}
impl<'a> FormProviderAdapter<'a> {
pub fn new(form_ui: &'a dyn FormUI) -> Self {
Self { form_ui }
}
}
impl<'a> FormProvider for FormProviderAdapter<'a> {
fn show(&self, layout: &str, fields: &Params, _: &Params) -> FormProviderResult {
let fields = convert_fields(fields);
match self.form_ui.show(layout, &fields) {
Ok(Some(results)) => FormProviderResult::Success(results),
Ok(None) => FormProviderResult::Aborted,
Err(err) => FormProviderResult::Error(err),
}
}
}
// TODO: test
fn convert_fields(fields: &Params) -> HashMap<String, FormField> {
let mut out = HashMap::new();
for (name, field) in fields {
let mut form_field = None;
if let Value::Object(params) = field {
if let Some(Value::String(field_type)) = params.get("type") {
form_field = match field_type.as_str() {
"text" => Some(FormField::Text {
default: params
.get("default")
.and_then(|val| val.as_string())
.cloned(),
multiline: params
.get("multiline")
.and_then(|val| val.as_bool())
.cloned()
.unwrap_or(false),
}),
"choice" => Some(FormField::Choice {
default: params
.get("default")
.and_then(|val| val.as_string())
.cloned(),
values: params
.get("values")
.and_then(|val| val.as_array())
.map(|arr| {
arr
.into_iter()
.flat_map(|choice| choice.as_string())
.cloned()
.collect()
})
.unwrap_or_default(),
}),
"list" => Some(FormField::List {
default: params
.get("default")
.and_then(|val| val.as_string())
.cloned(),
values: params
.get("values")
.and_then(|val| val.as_array())
.map(|arr| {
arr
.into_iter()
.flat_map(|choice| choice.as_string())
.cloned()
.collect()
})
.unwrap_or_default(),
}),
_ => None,
}
}
}
if let Some(form_field) = form_field {
out.insert(name.clone(), form_field);
} else {
error!("malformed form field format for '{}'", name);
}
}
out
}

View File

@ -17,5 +17,5 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
pub mod modulo;
pub mod form;
pub mod selector;

View File

@ -1,125 +0,0 @@
/*
* 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 std::{collections::HashMap, path::PathBuf};
use super::ModuloManager;
use anyhow::Result;
use espanso_render::extension::form::{FormProvider, FormProviderResult};
use log::{error};
use serde::Serialize;
use serde_json::{Map, Value};
pub struct ModuloFormProviderAdapter<'a> {
manager: &'a ModuloManager,
icon_path: Option<String>,
}
impl<'a> ModuloFormProviderAdapter<'a> {
pub fn new(manager: &'a ModuloManager, icon_path: Option<PathBuf>) -> Self {
Self {
manager,
icon_path: icon_path.map(|path| path.to_string_lossy().to_string()),
}
}
}
impl<'a> FormProvider for ModuloFormProviderAdapter<'a> {
fn show(
&self,
layout: &str,
fields: &espanso_render::Params,
_: &espanso_render::Params,
) -> FormProviderResult {
let modulo_form_config = ModuloFormConfig {
icon: self.icon_path.as_deref(),
title: "espanso",
layout,
fields: convert_params_into_object(fields),
};
match serde_json::to_string(&modulo_form_config) {
Ok(json_config) => {
match self
.manager
.invoke(&["form", "-j", "-i", "-"], &json_config)
{
Ok(output) => {
let json: Result<HashMap<String, String>, _> = serde_json::from_str(&output);
match json {
Ok(json) => {
if json.is_empty() {
return FormProviderResult::Aborted;
} else {
return FormProviderResult::Success(json);
}
}
Err(error) => {
return FormProviderResult::Error(error.into());
}
}
}
Err(err) => {
return FormProviderResult::Error(err.into());
}
}
}
Err(err) => {
return FormProviderResult::Error(err.into());
}
}
}
}
#[derive(Debug, Serialize)]
struct ModuloFormConfig<'a> {
icon: Option<&'a str>,
title: &'a str,
layout: &'a str,
fields: Map<String, Value>,
}
// TODO: test
fn convert_params_into_object(params: &espanso_render::Params) -> Map<String, Value> {
let mut obj = Map::new();
for (field, value) in params {
obj.insert(field.clone(), convert_value(value));
}
obj
}
// TODO: test
fn convert_value(value: &espanso_render::Value) -> Value {
match value {
espanso_render::Value::Null => Value::Null,
espanso_render::Value::Bool(value) => Value::Bool(*value),
espanso_render::Value::Number(num) => match num {
espanso_render::Number::Integer(val) => Value::Number((*val).into()),
espanso_render::Number::Float(val) => {
Value::Number(serde_json::Number::from_f64(*val).unwrap_or_else(|| {
error!("unable to convert float value to json");
0.into()
}))
}
},
espanso_render::Value::String(value) => Value::String(value.clone()),
espanso_render::Value::Array(arr) => Value::Array(arr.into_iter().map(convert_value).collect()),
espanso_render::Value::Object(obj) => Value::Object(convert_params_into_object(obj)),
}
}

55
espanso/src/gui/mod.rs Normal file
View File

@ -0,0 +1,55 @@
/*
* 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 std::collections::HashMap;
use anyhow::Result;
pub mod modulo;
pub trait SearchUI {
fn show(&self, items: &SearchItem) -> Result<Option<String>>;
}
#[derive(Debug)]
pub struct SearchItem {
pub id: String,
pub label: String,
pub tag: Option<String>,
}
pub trait FormUI {
fn show(&self, layout: &str, fields: &HashMap<String, FormField>) -> Result<Option<HashMap<String, String>>>;
}
#[derive(Debug)]
pub enum FormField {
Text {
default: Option<String>,
multiline: bool,
},
Choice {
default: Option<String>,
values: Vec<String>,
},
List {
default: Option<String>,
values: Vec<String>,
}
}

View File

@ -0,0 +1,107 @@
/*
* 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 serde::Serialize;
use serde_json::{json, Map, Value};
use std::{collections::HashMap, path::PathBuf};
use crate::gui::{FormField, FormUI};
use super::manager::ModuloManager;
pub struct ModuloFormUI<'a> {
manager: &'a ModuloManager,
icon_path: Option<String>,
}
impl<'a> ModuloFormUI<'a> {
pub fn new(manager: &'a ModuloManager, icon_path: Option<PathBuf>) -> Self {
Self {
manager,
icon_path: icon_path.map(|path| path.to_string_lossy().to_string()),
}
}
}
impl<'a> FormUI for ModuloFormUI<'a> {
fn show(
&self,
layout: &str,
fields: &HashMap<String, FormField>,
) -> anyhow::Result<Option<HashMap<String, String>>> {
let modulo_form_config = ModuloFormConfig {
icon: self.icon_path.as_deref(),
title: "espanso",
layout,
fields: convert_fields_into_object(fields),
};
let json_config = serde_json::to_string(&modulo_form_config)?;
let output = self
.manager
.invoke(&["form", "-j", "-i", "-"], &json_config)?;
let json: Result<HashMap<String, String>, _> = serde_json::from_str(&output);
match json {
Ok(json) => {
if json.is_empty() {
return Ok(None);
} else {
return Ok(Some(json));
}
}
Err(error) => {
return Err(error.into());
}
}
}
}
#[derive(Debug, Serialize)]
struct ModuloFormConfig<'a> {
icon: Option<&'a str>,
title: &'a str,
layout: &'a str,
fields: Map<String, Value>,
}
// TODO: test
fn convert_fields_into_object(fields: &HashMap<String, FormField>) -> Map<String, Value> {
let mut obj = Map::new();
for (name, field) in fields {
let value = match field {
FormField::Text { default, multiline } => json!({
"type": "text",
"default": default,
"multiline": multiline,
}),
FormField::Choice { default, values } => json!({
"type": "choice",
"default": default,
"values": values,
}),
FormField::List { default, values } => json!({
"type": "list",
"default": default,
"values": values,
}),
};
obj.insert(name.clone(), value);
}
obj
}

View File

@ -23,8 +23,6 @@ use std::io::Write;
use std::process::Command;
use thiserror::Error;
pub mod form;
pub struct ModuloManager {
modulo_path: Option<String>,
}

View File

@ -0,0 +1,23 @@
/*
* 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/>.
*/
pub mod form;
pub mod manager;
// TODO: implement FormUI and SearchUI traits using ModuloManager

View File

@ -32,6 +32,7 @@ use simplelog::{
mod cli;
mod engine;
mod gui;
mod logging;
mod util;