use rand::seq::SliceRandom; use seqdiff::ratio; use serde_yaml; use std::path::Path; use std::{fs, fs::File}; use core::time::Duration; use std::path::PathBuf; use std::process::Child; use std::process::Command; use tauri::Manager; mod structs; pub use structs::*; use crate::config; use crate::events; pub fn parse_commands() -> Result, String> { // collect commands let mut commands: Vec = vec![]; // read commands directories first if let Ok(cpaths) = fs::read_dir(config::COMMANDS_PATH) { for cpath in cpaths { // validate this command, check if required files exists let _cpath = cpath.unwrap().path(); let cc_file = Path::new(&_cpath).join("command.yaml"); if cc_file.exists() { // try parse config files let cc_reader = std::fs::File::open(&cc_file).unwrap(); let cc_yaml: CommandsList; // try parse command.yaml if let Ok(parse_result) = serde_yaml::from_reader::(cc_reader) { cc_yaml = parse_result; } else { println!("Can't parse {}, skipping ...", &cc_file.display()); continue; // return Err(format!("Can't parse {}", &cc_file.display())); } // everything seems to be Ok commands.push(AssistantCommand { path: _cpath, commands: cc_yaml, }); } } if commands.len() > 0 { Ok(commands) } else { Err("No commands were found".into()) } } else { return Err("Error reading commands directory".into()); } } pub fn fetch_command<'a>( phrase: &str, commands: &'a Vec, ) -> Option<(&'a PathBuf, &'a Config)> { // result scmd let mut result_scmd: Option<(&PathBuf, &Config)> = None; let mut current_max_ratio = config::CMD_RATIO_THRESHOLD; // convert fetch phrase to sequence let fetch_phrase_chars = phrase.chars().collect::>(); // list all the commands for cmd in commands { // list all subcommands for scmd in &cmd.commands.list { // list all phrases in command for cmd_phrase in &scmd.phrases { // convert cmd phrase to sequence let cmd_phrase_chars = cmd_phrase.chars().collect::>(); // compare fetch phrase with cmd phrase let ratio = ratio(&fetch_phrase_chars, &cmd_phrase_chars); // return, if it fits the given threshold if ratio >= current_max_ratio { result_scmd = Some((&cmd.path, &scmd)); current_max_ratio = ratio; // println!("Ratio is: {}", ratio); // return Some((&cmd.path, &scmd)) } } } } if let Some((cmd_path, scmd)) = result_scmd { println!("Ratio is: {}", current_max_ratio); Some((&cmd_path, &scmd)) } else { None } } pub fn execute_exe(exe: &str, args: &Vec) -> std::io::Result { Command::new(exe).args(args).spawn() } pub fn execute_command( cmd_path: &PathBuf, cmd_config: &Config, app_handle: &tauri::AppHandle, ) -> Result<(), String> { match cmd_config.command.action.as_str() { "voice" => { // VOICE command type let random_cmd_sound = cmd_config .voice .sounds .choose(&mut rand::thread_rng()) .unwrap(); events::play(random_cmd_sound, app_handle); Ok(()) } "ahk" => { // AutoHotkey command type let exe_path_absolute = Path::new(&cmd_config.command.exe_path); let exe_path_local = Path::new(&cmd_path).join(&cmd_config.command.exe_path); if let Ok(_) = execute_exe( if exe_path_absolute.exists() { exe_path_absolute.to_str().unwrap() } else { exe_path_local.to_str().unwrap() }, &cmd_config.command.exe_args, ) { let random_cmd_sound = cmd_config .voice .sounds .choose(&mut rand::thread_rng()) .unwrap(); events::play(random_cmd_sound, app_handle); Ok(()) } else { Err("AHK process spawn error (does exe path is valid?)".into()) } } "cli" => { // CLI command type let exe_path_absolute = Path::new(&cmd_config.command.exe_path); let exe_path_local = Path::new(&cmd_path).join(&cmd_config.command.exe_path); if let Ok(_) = execute_exe( if exe_path_absolute.exists() { exe_path_absolute.to_str().unwrap() } else { exe_path_local.to_str().unwrap() }, &cmd_config.command.exe_args, ) { let random_cmd_sound = cmd_config .voice .sounds .choose(&mut rand::thread_rng()) .unwrap(); events::play(random_cmd_sound, app_handle); Ok(()) } else { Err("Shell process spawn error (does cli command is valid?)".into()) } } "terminate" => { // TERMINATE command type let random_cmd_sound = cmd_config .voice .sounds .choose(&mut rand::thread_rng()) .unwrap(); events::play(random_cmd_sound, app_handle); std::thread::sleep(Duration::from_secs(2)); std::process::exit(0); } _ => Err("Command type unknown".into()), } }