// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::evals::find_similar; use itertools::Itertools; pub fn get_path_files() -> Vec { let env = std::env::var("_PR_EXECUTABLES"); if let Ok(env) = env { if !env.is_empty() { return env.split(' ').map(|s| s.to_owned()).collect(); } } let path_env = path_env(); #[cfg(debug_assertions)] eprintln!("path_env: {path_env}"); let path_env_sep = path_env_sep(); let path = path_env.split(path_env_sep).collect::>(); let mut all_executable = vec![]; for p in path { #[cfg(windows)] let p = path_convert(p); #[cfg(debug_assertions)] eprintln!("p={p}"); let files = match std::fs::read_dir(p) { Ok(files) => files, Err(_) => continue, }; for file in files { let file = file.unwrap(); #[allow(unused_mut)] let mut file_name = file.file_name().into_string().unwrap(); #[cfg(windows)] strip_extension(&mut file_name); all_executable.push(file_name); } } #[cfg(debug_assertions)] { let mut all_executable = all_executable.clone(); all_executable.sort_unstable(); eprintln!("all_executable={all_executable:?}"); } all_executable.iter().unique().cloned().collect() } pub fn best_match_file(input: &str) -> Option { let mut input = input.trim_matches(|c| c == '\'' || c == '"').to_owned(); #[cfg(debug_assertions)] eprintln!("best_match_file input: {input}"); let mut exit_dirs = Vec::new(); let mut files = loop { match std::fs::read_dir(&input) { Ok(files) => break files, Err(_) => { if let Some((dirs, exit_dir)) = input.rsplit_once(std::path::MAIN_SEPARATOR) { exit_dirs.push(exit_dir.to_owned()); input = dirs.to_owned(); } else { exit_dirs.push(input.to_owned()); input = ".".to_owned(); break std::fs::read_dir("./").unwrap(); } } } }; while let Some(exit_dir) = exit_dirs.pop() { let dir_files = files .map(|file| { let file = file.unwrap(); file.file_name().into_string().unwrap().replace(' ', "\\ ") }) .collect::>(); let best_match = find_similar(&exit_dir, &dir_files, Some(2)); best_match.as_ref()?; input = format!("{}/{}", input, best_match.unwrap()); files = match std::fs::read_dir(&input) { Ok(files) => files, Err(_) => return Some(input), }; } Some(input) } #[cfg(windows)] fn msys2_conv_path(p: &str) -> std::io::Result { std::process::Command::new("cygpath") .arg("-w") .arg(p) .output() .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_owned()) } #[cfg(windows)] fn is_msystem() -> bool { std::env::var("MSYSTEM").is_ok() } #[cfg(windows)] fn path_env() -> String { if is_msystem() { String::from_utf8_lossy( &std::process::Command::new("bash") .arg("-c") .arg("echo $PATH") .output() .unwrap() .stdout, ) .trim() .to_owned() } else { std::env::var("PATH").unwrap() } } #[cfg(windows)] pub fn path_env_sep() -> &'static str { if is_msystem() { ":" } else { ";" } } #[cfg(windows)] pub fn path_convert(path: &str) -> String { if is_msystem() { msys2_conv_path(path).expect("Failed to convert path for msys") } else { path.to_owned() } } #[cfg(windows)] fn strip_extension(file_name: &str) -> String { let mut file_name = file_name.to_owned(); let suffixies = [".exe", ".sh", ".ps1"]; for suffix in suffixies { if let Some(file_name_strip) = file_name.strip_suffix(suffix) { file_name = file_name_strip.to_owned(); break; } } if !file_name.contains(".") { file_name = file_name.to_owned(); } file_name } #[cfg(not(windows))] fn path_env() -> String { std::env::var("PATH").unwrap() } #[cfg(not(windows))] pub fn path_env_sep() -> &'static str { ":" }