use crate::config::Configs; use std::process::{Command, Child, Output}; use log::{error}; use std::io::{Error, Write}; pub mod form; pub struct ModuloManager { modulo_path: Option, } impl ModuloManager { pub fn new(config: &Configs) -> Self { let mut modulo_path: Option = None; // 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()); }else{ // Check in the same directory of espanso if let Ok(exe_path) = std::env::current_exe() { if let Some(parent) = exe_path.parent() { let possible_path = parent.join("modulo"); let possible_path = possible_path.to_string_lossy().to_string(); if let Ok(output) = Command::new(&possible_path).arg("--version").output() { if output.status.success() { modulo_path = Some(possible_path); } } } } // Otherwise check if present in the PATH if modulo_path.is_none() { if let Ok(output) = Command::new("modulo").arg("--version").output() { if output.status.success() { modulo_path = Some("modulo".to_owned()); } } } } Self { modulo_path, } } pub fn is_valid(&self) -> bool { self.modulo_path.is_some() } pub fn get_version(&self) -> Option { if let Some(ref modulo_path) = self.modulo_path { if let Ok(output) = Command::new(modulo_path).arg("--version").output() { let version = String::from_utf8_lossy(&output.stdout); return Some(version.to_string()); } } None } pub fn invoke(&self, args: &[&str], body: &str) -> Option { if self.modulo_path.is_none() { error!("Attempt to invoke modulo even though it's not configured"); return None; } if let Some(ref modulo_path) = self.modulo_path { let child = Command::new(modulo_path) .args(args) .stdin(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped()) .spawn(); match child { Ok(mut child) => { if let Some(stdin) = child.stdin.as_mut() { match stdin.write_all(body.as_bytes()) { Ok(_) => { // Get the output match child.wait_with_output() { Ok(child_output) => { let output = String::from_utf8_lossy(&child_output.stdout); // Check also if the program reports an error let error = String::from_utf8_lossy(&child_output.stderr); if !error.is_empty() { error!("modulo reported an error: {}", error); } return Some(output.to_string()); }, Err(error) => { error!("error while getting output from modulo: {}", error); }, } }, Err(error) => { error!("error while sending body to modulo"); }, } }else{ error!("unable to open stdin to modulo"); } }, Err(error) => { error!("error reported when invoking modulo: {}", error); }, } } None } }