use std::{ fs::read_dir, path::PathBuf, }; use freedesktop_entry_parser as fd; use mlua::prelude::*; use parking_lot::Mutex; use rayon::prelude::*; use url::Url; use crate::entry::{ entries_to_lua_table, Entry, }; fn parse_entry<'a>(entry: fd::Entry, path: PathBuf) -> Result { let section = entry.section("Desktop Entry"); let name = section.attr("Name").ok_or(())?.to_string(); if section.attr("Type").ok_or(())? != "Application" { return Err(()); } match section.attr("OnlyShowIn") { Some(_) => return Err(()), None => {} } match section.attr("Hidden") { Some(_) => return Err(()), None => {} } match section.attr("NoDisplay") { Some(_) => return Err(()), None => {} } let exec = section.attr("Exec").ok_or(())?.to_string(); let mut new_exec = exec.clone(); for (index, _) in exec.match_indices("%") { match exec.chars().nth(index + 1).unwrap().to_ascii_lowercase() { 'i' => match section.attr("Icon") { Some(icon) => new_exec.replace_range(index..index + 2, &format!("--icon {}", icon)), None => {} }, 'c' => new_exec.replace_range(index..index + 2, &name), 'k' => new_exec.replace_range(index..index + 2, Url::from_file_path(&path)?.as_str()), 'f' | 'u' | 'v' | 'm' | 'd' | 'n' => new_exec.replace_range(index..index + 2, ""), _ => continue, } } Ok(Entry { message: name, exec: Some(( new_exec, section .attr("Terminal") .unwrap_or("false") .parse() .map_err(drop)?, )), provider: "Application".to_string(), }) } pub fn query(lua: &Lua, input: String) -> LuaResult { let applications_dir = "/usr/share/applications"; let entries = read_dir(applications_dir)? .map(|result| result.map(|e| e.path())) .collect::, std::io::Error>>()?; let entries = entries .into_iter() .filter(|e| match e.extension() { Some(ext) if ext == "desktop" => true, None | _ => false, }) .collect::>(); let mut parsed_entries: Mutex> = Mutex::new(Vec::new()); entries.into_par_iter().for_each(|path| { let entry = match fd::parse_entry(&path) { Ok(entry) => entry, Err(_) => return, }; match parse_entry(entry, path) { Ok(parsed_entry) => parsed_entries.lock().push(parsed_entry), Err(_) => return, } }); Ok(entries_to_lua_table( parsed_entries .get_mut() .iter() .filter(|entry| entry.message.to_lowercase().contains(&input)) .map(|entry| (*entry).clone()) .collect(), lua, )) }