parent
b38623376e
commit
8edf998e60
71
espanso/src/cli/match_cli/exec.rs
Normal file
71
espanso/src/cli/match_cli/exec.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* 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::{bail, Context, Result};
|
||||||
|
use clap::ArgMatches;
|
||||||
|
use espanso_ipc::IPCClient;
|
||||||
|
use espanso_path::Paths;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ipc::{create_ipc_client_to_worker, IPCEvent, RequestMatchExpansionPayload},
|
||||||
|
lock::acquire_worker_lock,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn exec_main(cli_args: &ArgMatches, paths: &Paths) -> Result<()> {
|
||||||
|
let trigger = cli_args.value_of("trigger");
|
||||||
|
let args = cli_args.values_of("arg");
|
||||||
|
|
||||||
|
if trigger.is_none() || trigger.map(str::is_empty).unwrap_or(false) {
|
||||||
|
bail!("You need to specify the --trigger 'trigger' option. Run `espanso match exec --help` for more information.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if acquire_worker_lock(&paths.runtime).is_some() {
|
||||||
|
bail!("Worker process is not running, please start Espanso first.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut client = create_ipc_client_to_worker(&paths.runtime)?;
|
||||||
|
|
||||||
|
let mut match_args = HashMap::new();
|
||||||
|
if let Some(args) = args {
|
||||||
|
args.for_each(|arg| {
|
||||||
|
let tokens = arg.split_once('=');
|
||||||
|
if let Some((key, value)) = tokens {
|
||||||
|
match_args.insert(key.to_string(), value.to_string());
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"invalid format for argument '{}', you should follow the 'name=value' format",
|
||||||
|
arg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
client
|
||||||
|
.send_async(IPCEvent::RequestMatchExpansion(
|
||||||
|
RequestMatchExpansionPayload {
|
||||||
|
trigger: trigger.map(String::from),
|
||||||
|
args: match_args,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.context("unable to send payload to worker process")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -19,10 +19,12 @@
|
||||||
|
|
||||||
use super::{CliModule, CliModuleArgs};
|
use super::{CliModule, CliModuleArgs};
|
||||||
|
|
||||||
|
mod exec;
|
||||||
mod list;
|
mod list;
|
||||||
|
|
||||||
pub fn new() -> CliModule {
|
pub fn new() -> CliModule {
|
||||||
CliModule {
|
CliModule {
|
||||||
|
requires_paths: true,
|
||||||
requires_config: true,
|
requires_config: true,
|
||||||
subcommand: "match".to_string(),
|
subcommand: "match".to_string(),
|
||||||
entry: match_main,
|
entry: match_main,
|
||||||
|
@ -34,14 +36,18 @@ fn match_main(args: CliModuleArgs) -> i32 {
|
||||||
let cli_args = args.cli_args.expect("missing cli_args");
|
let cli_args = args.cli_args.expect("missing cli_args");
|
||||||
let config_store = args.config_store.expect("missing config_store");
|
let config_store = args.config_store.expect("missing config_store");
|
||||||
let match_store = args.match_store.expect("missing match_store");
|
let match_store = args.match_store.expect("missing match_store");
|
||||||
|
let paths = args.paths.expect("missing paths");
|
||||||
|
|
||||||
if let Some(sub_args) = cli_args.subcommand_matches("list") {
|
if let Some(sub_args) = cli_args.subcommand_matches("list") {
|
||||||
if let Err(err) = list::list_main(sub_args, config_store, match_store) {
|
if let Err(err) = list::list_main(sub_args, config_store, match_store) {
|
||||||
eprintln!("unable to list matches: {:?}", err);
|
eprintln!("unable to list matches: {:?}", err);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else if let Some(_sub_args) = cli_args.subcommand_matches("exec") {
|
} else if let Some(sub_args) = cli_args.subcommand_matches("exec") {
|
||||||
todo!();
|
if let Err(err) = exec::exec_main(sub_args, &paths) {
|
||||||
|
eprintln!("unable to exec match: {:?}", err);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Invalid use, please run 'espanso match --help' to get more information.");
|
eprintln!("Invalid use, please run 'espanso match --help' to get more information.");
|
||||||
return 1;
|
return 1;
|
||||||
|
|
72
espanso/src/cli/worker/engine/funnel/ipc.rs
Normal file
72
espanso/src/cli/worker/engine/funnel/ipc.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* 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 crossbeam::channel::{Receiver, Select, SelectedOperation};
|
||||||
|
|
||||||
|
use espanso_engine::{
|
||||||
|
event::{Event, EventType},
|
||||||
|
funnel,
|
||||||
|
};
|
||||||
|
use log::warn;
|
||||||
|
|
||||||
|
use super::sequencer::Sequencer;
|
||||||
|
|
||||||
|
pub struct IpcEventSource<'a> {
|
||||||
|
pub ipc_event_receiver: Receiver<EventType>,
|
||||||
|
pub sequencer: &'a Sequencer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IpcEventSource<'a> {
|
||||||
|
pub fn new(ipc_event_receiver: Receiver<EventType>, sequencer: &'a Sequencer) -> Self {
|
||||||
|
IpcEventSource {
|
||||||
|
ipc_event_receiver,
|
||||||
|
sequencer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> funnel::Source<'a> for IpcEventSource<'a> {
|
||||||
|
fn register(&'a self, select: &mut Select<'a>) -> usize {
|
||||||
|
select.recv(&self.ipc_event_receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive(&self, op: SelectedOperation) -> Option<Event> {
|
||||||
|
let ipc_event = op
|
||||||
|
.recv(&self.ipc_event_receiver)
|
||||||
|
.expect("unable to select data from IpcEventSource receiver");
|
||||||
|
|
||||||
|
// Execute only events that have been whitelisted
|
||||||
|
if !is_event_type_allowed(&ipc_event) {
|
||||||
|
warn!(
|
||||||
|
"received black-listed event from IPC stream, blocking it: {:?}",
|
||||||
|
ipc_event
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Event {
|
||||||
|
source_id: self.sequencer.next_id(),
|
||||||
|
etype: ipc_event,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_event_type_allowed(event: &EventType) -> bool {
|
||||||
|
matches!(event, EventType::MatchExecRequest(_))
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ use self::{
|
||||||
|
|
||||||
pub mod detect;
|
pub mod detect;
|
||||||
pub mod exit;
|
pub mod exit;
|
||||||
|
pub mod ipc;
|
||||||
pub mod key_state;
|
pub mod key_state;
|
||||||
pub mod modifier;
|
pub mod modifier;
|
||||||
pub mod secure_input;
|
pub mod secure_input;
|
||||||
|
|
|
@ -43,33 +43,22 @@ impl<'a> SecureInputSource<'a> {
|
||||||
|
|
||||||
impl<'a> funnel::Source<'a> for SecureInputSource<'a> {
|
impl<'a> funnel::Source<'a> for SecureInputSource<'a> {
|
||||||
fn register(&'a self, select: &mut Select<'a>) -> usize {
|
fn register(&'a self, select: &mut Select<'a>) -> usize {
|
||||||
if cfg!(target_os = "macos") {
|
select.recv(&self.receiver)
|
||||||
select.recv(&self.receiver)
|
|
||||||
} else {
|
|
||||||
999999
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn receive(&self, op: SelectedOperation) -> Option<Event> {
|
fn receive(&self, op: SelectedOperation) -> Option<Event> {
|
||||||
if cfg!(target_os = "macos") {
|
let si_event = op
|
||||||
let si_event = op
|
.recv(&self.receiver)
|
||||||
.recv(&self.receiver)
|
.expect("unable to select data from SecureInputSource receiver");
|
||||||
.expect("unable to select data from SecureInputSource receiver");
|
|
||||||
|
|
||||||
Some(Event {
|
Some(Event {
|
||||||
source_id: self.sequencer.next_id(),
|
source_id: self.sequencer.next_id(),
|
||||||
etype: match si_event {
|
etype: match si_event {
|
||||||
SecureInputEvent::Disabled => EventType::SecureInputDisabled,
|
SecureInputEvent::Disabled => EventType::SecureInputDisabled,
|
||||||
SecureInputEvent::Enabled { app_name, app_path } => {
|
SecureInputEvent::Enabled { app_name, app_path } => {
|
||||||
EventType::SecureInputEnabled(SecureInputEnabledEvent { app_name, app_path })
|
EventType::SecureInputEnabled(SecureInputEnabledEvent { app_name, app_path })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
Some(Event {
|
|
||||||
source_id: self.sequencer.next_id(),
|
|
||||||
etype: EventType::NOOP,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ use anyhow::Result;
|
||||||
use crossbeam::channel::Receiver;
|
use crossbeam::channel::Receiver;
|
||||||
use espanso_config::{config::ConfigStore, matches::store::MatchStore};
|
use espanso_config::{config::ConfigStore, matches::store::MatchStore};
|
||||||
use espanso_detect::SourceCreationOptions;
|
use espanso_detect::SourceCreationOptions;
|
||||||
use espanso_engine::event::ExitMode;
|
use espanso_engine::event::{EventType, ExitMode};
|
||||||
use espanso_inject::{InjectorCreationOptions, KeyboardStateProvider};
|
use espanso_inject::{InjectorCreationOptions, KeyboardStateProvider};
|
||||||
use espanso_path::Paths;
|
use espanso_path::Paths;
|
||||||
use espanso_ui::{event::UIEvent, UIRemote};
|
use espanso_ui::{event::UIEvent, UIRemote};
|
||||||
|
@ -82,6 +82,7 @@ pub fn initialize_and_spawn(
|
||||||
secure_input_receiver: Receiver<SecureInputEvent>,
|
secure_input_receiver: Receiver<SecureInputEvent>,
|
||||||
use_evdev_backend: bool,
|
use_evdev_backend: bool,
|
||||||
start_reason: Option<String>,
|
start_reason: Option<String>,
|
||||||
|
ipc_event_receiver: Receiver<EventType>,
|
||||||
) -> Result<JoinHandle<ExitMode>> {
|
) -> Result<JoinHandle<ExitMode>> {
|
||||||
let handle = std::thread::Builder::new()
|
let handle = std::thread::Builder::new()
|
||||||
.name("engine thread".to_string())
|
.name("engine thread".to_string())
|
||||||
|
@ -131,17 +132,18 @@ pub fn initialize_and_spawn(
|
||||||
})
|
})
|
||||||
.expect("failed to initialize detector module");
|
.expect("failed to initialize detector module");
|
||||||
let exit_source = super::engine::funnel::exit::ExitSource::new(exit_signal, &sequencer);
|
let exit_source = super::engine::funnel::exit::ExitSource::new(exit_signal, &sequencer);
|
||||||
|
let ipc_event_source =
|
||||||
|
super::engine::funnel::ipc::IpcEventSource::new(ipc_event_receiver, &sequencer);
|
||||||
let ui_source = super::engine::funnel::ui::UISource::new(ui_event_receiver, &sequencer);
|
let ui_source = super::engine::funnel::ui::UISource::new(ui_event_receiver, &sequencer);
|
||||||
let secure_input_source = super::engine::funnel::secure_input::SecureInputSource::new(
|
let secure_input_source = super::engine::funnel::secure_input::SecureInputSource::new(
|
||||||
secure_input_receiver,
|
secure_input_receiver,
|
||||||
&sequencer,
|
&sequencer,
|
||||||
);
|
);
|
||||||
let sources: Vec<&dyn espanso_engine::funnel::Source> = vec![
|
let mut sources: Vec<&dyn espanso_engine::funnel::Source> =
|
||||||
&detect_source,
|
vec![&detect_source, &exit_source, &ui_source, &ipc_event_source];
|
||||||
&exit_source,
|
if cfg!(target_os = "macos") {
|
||||||
&ui_source,
|
sources.push(&secure_input_source);
|
||||||
&secure_input_source,
|
}
|
||||||
];
|
|
||||||
let funnel = espanso_engine::funnel::default(&sources);
|
let funnel = espanso_engine::funnel::default(&sources);
|
||||||
|
|
||||||
let rolling_matcher = RollingMatcherAdapter::new(
|
let rolling_matcher = RollingMatcherAdapter::new(
|
||||||
|
@ -226,6 +228,7 @@ pub fn initialize_and_spawn(
|
||||||
&config_manager,
|
&config_manager,
|
||||||
&config_manager,
|
&config_manager,
|
||||||
&modifier_state_store,
|
&modifier_state_store,
|
||||||
|
&combined_match_cache,
|
||||||
);
|
);
|
||||||
|
|
||||||
let event_injector = EventInjectorAdapter::new(&*injector, &config_manager);
|
let event_injector = EventInjectorAdapter::new(&*injector, &config_manager);
|
||||||
|
|
|
@ -21,13 +21,17 @@ use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use crossbeam::channel::Sender;
|
use crossbeam::channel::Sender;
|
||||||
use espanso_engine::event::ExitMode;
|
use espanso_engine::event::{external::MatchExecRequestEvent, EventType, ExitMode};
|
||||||
use espanso_ipc::{EventHandlerResponse, IPCServer};
|
use espanso_ipc::{EventHandlerResponse, IPCServer};
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
|
|
||||||
use crate::ipc::IPCEvent;
|
use crate::ipc::IPCEvent;
|
||||||
|
|
||||||
pub fn initialize_and_spawn(runtime_dir: &Path, exit_notify: Sender<ExitMode>) -> Result<()> {
|
pub fn initialize_and_spawn(
|
||||||
|
runtime_dir: &Path,
|
||||||
|
exit_notify: Sender<ExitMode>,
|
||||||
|
event_notify: Sender<EventType>,
|
||||||
|
) -> Result<()> {
|
||||||
let server = crate::ipc::create_worker_ipc_server(runtime_dir)?;
|
let server = crate::ipc::create_worker_ipc_server(runtime_dir)?;
|
||||||
|
|
||||||
std::thread::Builder::new()
|
std::thread::Builder::new()
|
||||||
|
@ -55,6 +59,21 @@ pub fn initialize_and_spawn(runtime_dir: &Path, exit_notify: Sender<ExitMode>) -
|
||||||
|
|
||||||
EventHandlerResponse::NoResponse
|
EventHandlerResponse::NoResponse
|
||||||
}
|
}
|
||||||
|
IPCEvent::RequestMatchExpansion(payload) => {
|
||||||
|
if let Err(err) =
|
||||||
|
event_notify.send(EventType::MatchExecRequest(MatchExecRequestEvent {
|
||||||
|
trigger: payload.trigger,
|
||||||
|
args: payload.args,
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
error!(
|
||||||
|
"experienced error while sending event signal from worker ipc handler: {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventHandlerResponse::NoResponse
|
||||||
|
}
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
unexpected_event => {
|
unexpected_event => {
|
||||||
warn!(
|
warn!(
|
||||||
|
|
|
@ -21,8 +21,9 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use espanso_config::{
|
use espanso_config::{
|
||||||
config::ConfigStore,
|
config::ConfigStore,
|
||||||
matches::{store::MatchStore, Match, MatchEffect},
|
matches::{store::MatchStore, Match, MatchCause, MatchEffect},
|
||||||
};
|
};
|
||||||
|
use espanso_engine::event::internal::DetectedMatch;
|
||||||
|
|
||||||
use super::{builtin::BuiltInMatch, engine::process::middleware::match_select::MatchSummary};
|
use super::{builtin::BuiltInMatch, engine::process::middleware::match_select::MatchSummary};
|
||||||
|
|
||||||
|
@ -164,3 +165,50 @@ impl<'a> espanso_engine::process::MatchProvider for CombinedMatchCache<'a> {
|
||||||
ids
|
ids
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> espanso_engine::process::MatchResolver for CombinedMatchCache<'a> {
|
||||||
|
fn find_matches_from_trigger(&self, trigger: &str) -> Vec<DetectedMatch> {
|
||||||
|
let user_matches: Vec<DetectedMatch> = self
|
||||||
|
.user_match_cache
|
||||||
|
.cache
|
||||||
|
.values()
|
||||||
|
.filter_map(|m| {
|
||||||
|
if let MatchCause::Trigger(trigger_cause) = &m.cause {
|
||||||
|
if trigger_cause.triggers.iter().any(|t| t == trigger) {
|
||||||
|
Some(DetectedMatch {
|
||||||
|
id: m.id,
|
||||||
|
trigger: Some(trigger.to_string()),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let builtin_matches: Vec<DetectedMatch> = self
|
||||||
|
.builtin_match_cache
|
||||||
|
.values()
|
||||||
|
.filter_map(|m| {
|
||||||
|
if m.triggers.iter().any(|t| t == trigger) {
|
||||||
|
Some(DetectedMatch {
|
||||||
|
id: m.id,
|
||||||
|
trigger: Some(trigger.to_string()),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut matches = Vec::with_capacity(user_matches.len() + builtin_matches.len());
|
||||||
|
matches.extend(user_matches);
|
||||||
|
matches.extend(builtin_matches);
|
||||||
|
|
||||||
|
matches
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -112,6 +112,7 @@ fn worker_main(args: CliModuleArgs) -> i32 {
|
||||||
.expect("unable to initialize UI module");
|
.expect("unable to initialize UI module");
|
||||||
|
|
||||||
let (engine_exit_notify, engine_exit_receiver) = unbounded();
|
let (engine_exit_notify, engine_exit_receiver) = unbounded();
|
||||||
|
let (ipc_event_notify, ipc_event_receiver) = unbounded();
|
||||||
let (engine_ui_event_sender, engine_ui_event_receiver) = unbounded();
|
let (engine_ui_event_sender, engine_ui_event_receiver) = unbounded();
|
||||||
let (engine_secure_input_sender, engine_secure_input_receiver) = unbounded();
|
let (engine_secure_input_sender, engine_secure_input_receiver) = unbounded();
|
||||||
|
|
||||||
|
@ -126,11 +127,12 @@ fn worker_main(args: CliModuleArgs) -> i32 {
|
||||||
engine_secure_input_receiver,
|
engine_secure_input_receiver,
|
||||||
use_evdev_backend,
|
use_evdev_backend,
|
||||||
start_reason,
|
start_reason,
|
||||||
|
ipc_event_receiver,
|
||||||
)
|
)
|
||||||
.expect("unable to initialize engine");
|
.expect("unable to initialize engine");
|
||||||
|
|
||||||
// Setup the IPC server
|
// Setup the IPC server
|
||||||
ipc::initialize_and_spawn(&paths.runtime, engine_exit_notify.clone())
|
ipc::initialize_and_spawn(&paths.runtime, engine_exit_notify.clone(), ipc_event_notify)
|
||||||
.expect("unable to initialize IPC server");
|
.expect("unable to initialize IPC server");
|
||||||
|
|
||||||
// If specified, automatically monitor the daemon status and
|
// If specified, automatically monitor the daemon status and
|
||||||
|
|
|
@ -20,12 +20,20 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use espanso_ipc::{IPCClient, IPCServer};
|
use espanso_ipc::{IPCClient, IPCServer};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::Path;
|
use std::{collections::HashMap, path::Path};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum IPCEvent {
|
pub enum IPCEvent {
|
||||||
Exit,
|
Exit,
|
||||||
ExitAllProcesses,
|
ExitAllProcesses,
|
||||||
|
|
||||||
|
RequestMatchExpansion(RequestMatchExpansionPayload),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct RequestMatchExpansionPayload {
|
||||||
|
pub trigger: Option<String>,
|
||||||
|
pub args: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_daemon_ipc_server(runtime_dir: &Path) -> Result<impl IPCServer<IPCEvent>> {
|
pub fn create_daemon_ipc_server(runtime_dir: &Path) -> Result<impl IPCServer<IPCEvent>> {
|
||||||
|
|
|
@ -398,12 +398,24 @@ For example, specifying 'email' is equivalent to 'match/email.yml'."#))
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
// .subcommand(SubCommand::with_name("exec")
|
.subcommand(SubCommand::with_name("exec")
|
||||||
// .about("Triggers the expansion of the given match")
|
.about("Triggers the expansion of a match")
|
||||||
// .arg(Arg::with_name("trigger")
|
.arg(Arg::with_name("trigger")
|
||||||
// .help("The trigger of the match to be expanded")
|
.short("t")
|
||||||
// )
|
.long("trigger")
|
||||||
// )
|
.help("The trigger of the match to be expanded")
|
||||||
|
.required(false)
|
||||||
|
.takes_value(true)
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name("arg")
|
||||||
|
.long("arg")
|
||||||
|
.help("Specify also an argument for the expansion, following the --arg 'name=value' format. You can specify multiple ones.")
|
||||||
|
.required(false)
|
||||||
|
.takes_value(true)
|
||||||
|
.multiple(true)
|
||||||
|
.number_of_values(1)
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("package")
|
SubCommand::with_name("package")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user