2023-03-04 15:04:55 -06:00
|
|
|
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;
|
|
|
|
|
2023-04-04 08:43:40 -05:00
|
|
|
use crate::lenses::entry::{
|
2023-03-04 15:04:55 -06:00
|
|
|
entries_to_lua_table,
|
|
|
|
Entry,
|
|
|
|
};
|
|
|
|
|
2023-04-04 08:43:40 -05:00
|
|
|
fn parse_entry(entry: &fd::Entry, path: &PathBuf) -> Result<Entry, ()> {
|
2023-03-04 15:04:55 -06:00
|
|
|
let section = entry.section("Desktop Entry");
|
|
|
|
let name = section.attr("Name").ok_or(())?.to_string();
|
|
|
|
|
|
|
|
if section.attr("Type").ok_or(())? != "Application" {
|
|
|
|
return Err(());
|
|
|
|
}
|
|
|
|
|
2023-04-04 08:43:40 -05:00
|
|
|
if section.attr("OnlyShowIn").is_some()
|
|
|
|
|| section.attr("Hidden").is_some()
|
|
|
|
|| section.attr("NoDisplay").is_some()
|
|
|
|
{
|
|
|
|
return Err(());
|
2023-03-04 15:04:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
let exec = section.attr("Exec").ok_or(())?.to_string();
|
|
|
|
let mut new_exec = exec.clone();
|
2023-04-04 08:43:40 -05:00
|
|
|
for (index, _) in exec.match_indices('%') {
|
2023-03-04 15:04:55 -06:00
|
|
|
match exec.chars().nth(index + 1).unwrap().to_ascii_lowercase() {
|
2023-04-04 08:43:40 -05:00
|
|
|
'i' => {
|
|
|
|
if let Some(icon) = section.attr("Icon") {
|
|
|
|
new_exec.replace_range(index..index + 2, &format!("--icon {icon}"));
|
|
|
|
}
|
|
|
|
}
|
2023-03-04 15:04:55 -06:00
|
|
|
'c' => new_exec.replace_range(index..index + 2, &name),
|
2023-04-04 08:43:40 -05:00
|
|
|
'k' => new_exec.replace_range(index..index + 2, Url::from_file_path(path)?.as_str()),
|
2023-03-04 15:04:55 -06:00
|
|
|
|
|
|
|
'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<LuaTable> {
|
|
|
|
let applications_dir = "/usr/share/applications";
|
|
|
|
let entries = read_dir(applications_dir)?
|
|
|
|
.map(|result| result.map(|e| e.path()))
|
|
|
|
.collect::<Result<Vec<_>, std::io::Error>>()?;
|
|
|
|
|
|
|
|
let entries = entries
|
|
|
|
.into_iter()
|
2023-04-04 08:43:40 -05:00
|
|
|
.filter(|e| matches!(e.extension(), Some(ext) if ext == "desktop"))
|
2023-03-04 15:04:55 -06:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
let mut parsed_entries: Mutex<Vec<Entry>> = Mutex::new(Vec::new());
|
|
|
|
|
|
|
|
entries.into_par_iter().for_each(|path| {
|
2023-04-04 08:43:40 -05:00
|
|
|
let Ok(entry) = fd::parse_entry(&path) else {
|
|
|
|
return
|
2023-03-04 15:04:55 -06:00
|
|
|
};
|
|
|
|
|
2023-04-04 08:43:40 -05:00
|
|
|
if let Ok(parsed_entry) = parse_entry(&entry, &path) {
|
|
|
|
parsed_entries.lock().push(parsed_entry);
|
2023-03-04 15:04:55 -06:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Ok(entries_to_lua_table(
|
|
|
|
parsed_entries
|
|
|
|
.get_mut()
|
|
|
|
.iter()
|
|
|
|
.filter(|entry| entry.message.to_lowercase().contains(&input))
|
|
|
|
.map(|entry| (*entry).clone())
|
|
|
|
.collect(),
|
|
|
|
lua,
|
|
|
|
))
|
|
|
|
}
|