use std::{ fs::read_dir, path::PathBuf }; use freedesktop_entry_parser as fd; use mlua::prelude::*; use rayon::prelude::*; use url::Url; use crate::lenses::{ Entry, Lense, Cache }; #[derive(Default)] pub struct Application(pub Cache); impl Lense for Application { const NAME: &str = "Application"; fn get_cache(&self) -> &Cache { &self.0 } fn set_cache(&mut self, cache: Cache) { self.0 = cache; } fn query(_: &Lua, input: String) -> Result, anyhow::Error> { let applications_dir = "/usr/share/applications"; let entries = read_dir(applications_dir)? .map(|result| result.map(|e| e.path())) .collect::, std::io::Error>>()?; let parsed_entries: Vec = entries .into_par_iter() .filter(|path| path.extension().is_some_and(|ext| ext == "desktop")) .filter_map(|path| { parse_entry(path).ok().flatten() }) .collect(); Ok( parsed_entries .into_iter() .filter(|entry| entry.message.to_lowercase().contains(&input.to_lowercase())) .collect(), ) } } fn parse_entry(path: PathBuf) -> Result, ()> { let Ok(entry) = fd::parse_entry(&path) else { return Err(()) }; let section = entry.section("Desktop Entry"); let name = section.attr("Name").ok_or(())?.to_string(); if section.attr("Type").ok_or(())? != "Application" { return Err(()); } if section.attr("OnlyShowIn").is_some() || section.attr("Hidden").is_some() || section.attr("NoDisplay").is_some() { return Err(()); } 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' => { if let Some(icon) = section.attr("Icon") { new_exec.replace_range(index..index + 2, &format!("--icon {icon}")); } } '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(Some(Entry { message: name, exec: Some(( new_exec, section .attr("Terminal") .unwrap_or("false") .parse() .map_err(drop)?, )), })) }