From 370ebfa5ec9062b96e9ff95d3c66231dc27c172f Mon Sep 17 00:00:00 2001 From: imgurbot12 Date: Wed, 10 Jul 2024 01:26:53 -0700 Subject: [PATCH] feat: upgraded freedesktop parser --- plugin-desktop/Cargo.toml | 2 +- plugin-desktop/src/icons.rs | 43 ++++++++++++++++++--------------- plugin-desktop/src/main.rs | 48 ++++++++++++++++++------------------- 3 files changed, 48 insertions(+), 45 deletions(-) diff --git a/plugin-desktop/Cargo.toml b/plugin-desktop/Cargo.toml index 0ed8736..a5d01b9 100644 --- a/plugin-desktop/Cargo.toml +++ b/plugin-desktop/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] clap = { version = "4.5.9", features = ["derive"] } -freedesktop-desktop-entry = "0.5.2" +freedesktop-desktop-entry = "0.6.2" itertools = "0.13.0" once_cell = "1.19.0" rayon = "1.10.0" diff --git a/plugin-desktop/src/icons.rs b/plugin-desktop/src/icons.rs index 3f7e023..4f98aad 100644 --- a/plugin-desktop/src/icons.rs +++ b/plugin-desktop/src/icons.rs @@ -51,23 +51,23 @@ fn theme_inis(cfgdir: &PathBuf) -> Vec { } /// Parse FreeDesktop Theme-Name from Index File -fn get_theme_name(path: &PathBuf) -> Option { +fn get_theme_name(path: &PathBuf, locales: &[&str]) -> Option { let content = read_to_string(path).ok()?; - let config = DesktopEntry::decode(&path, &content).ok()?; + let config = DesktopEntry::from_str(&path, &content, locales).ok()?; config .groups .get(INDEX_MAIN) .and_then(|g| g.get(INDEX_NAME)) - .map(|key| key.0.to_owned()) + .map(|key| key.0.to_string()) } /// Determine XDG Icon Theme based on Preexisting Configuration Files -pub fn active_themes(cfgdir: &PathBuf, icondirs: &Vec) -> Vec { +pub fn active_themes(cfgdir: &PathBuf, icondirs: &Vec, locales: &[&str]) -> Vec { let mut themes: Vec = icondirs .iter() .map(|d| d.join(DEFAULT_INDEX)) .filter(|p| p.exists()) - .filter_map(|p| get_theme_name(&p)) + .filter_map(|p| get_theme_name(&p, locales)) .collect(); themes.extend(theme_inis(cfgdir)); let default = DEFAULT_THEME.to_string(); @@ -82,7 +82,7 @@ pub enum ThemeError { #[error("Failed to Read Index")] FileError(#[from] std::io::Error), #[error("Failed to Parse Index")] - IndexError(#[from] freedesktop_desktop_entry::DecodeError), + IndexError(String), #[error("No Such Group")] NoSuchGroup(&'static str), #[error("No Such Key")] @@ -138,11 +138,11 @@ fn sort_dirs(dirs: &mut Vec) -> Vec { } /// Parse Theme Index and Sort Directories based on Size Preference -fn parse_index(spec: &ThemeSpec) -> Result { +fn parse_index(spec: &ThemeSpec, locales: &[&str]) -> Result { // parse file content let index = spec.root.join(INDEX_FILE); - let content = read_to_string(&index)?; - let config = DesktopEntry::decode(&index, &content)?; + let config = DesktopEntry::from_path(index, locales) + .map_err(|e| ThemeError::IndexError(e.to_string()))?; let main = config .groups .get(INDEX_MAIN) @@ -151,7 +151,8 @@ fn parse_index(spec: &ThemeSpec) -> Result { let name = main .get(INDEX_NAME) .ok_or_else(|| ThemeError::NoSuchKey(INDEX_NAME))? - .0; + .0 + .to_string(); // check if name in supported themes let index = spec .themes @@ -179,7 +180,7 @@ fn parse_index(spec: &ThemeSpec) -> Result { .collect(); Ok(ThemeInfo { priority: index, - name: name.to_owned(), + name, paths: sort_dirs(&mut directories), }) } @@ -238,15 +239,15 @@ impl IconSpec { } } - pub fn standard(cfg: &PathBuf, sizes: Vec) -> Self { + pub fn standard(cfg: &PathBuf, sizes: Vec, locales: &[&str]) -> Self { let icon_paths = crate::data_dirs("icons"); - let themes = active_themes(cfg, &icon_paths); + let themes = active_themes(cfg, &icon_paths, locales); Self::new(icon_paths, themes, sizes) } } /// Parse and Collect a list of Directories to Find Icons in Order of Preference -fn parse_themes(icons: IconSpec) -> Vec { +fn parse_themes(icons: IconSpec, locales: &[&str]) -> Vec { // retrieve supported theme information let mut infos: Vec = icons .paths @@ -258,7 +259,7 @@ fn parse_themes(icons: IconSpec) -> Vec { // parse or guess index themes .filter_map(|icondir| { let spec = ThemeSpec::new(&icondir, &icons.themes, &icons.sizes); - parse_index(&spec) + parse_index(&spec, locales) .map(|r| Ok(r)) .unwrap_or_else(|_| guess_index(&spec)) .ok() @@ -284,20 +285,24 @@ fn is_icon(fname: &str) -> bool { } /// Collect Unique Icon Map based on Preffered Paths -pub fn collect_icons(spec: IconSpec) -> IconMap { +pub fn collect_icons(spec: IconSpec, locales: &[&str]) -> IconMap { let mut map = HashMap::new(); - for path in parse_themes(spec).into_iter() { + for path in parse_themes(spec, locales).into_iter() { let icons = WalkDir::new(path) .follow_links(true) .into_iter() .filter_map(|e| e.ok()) .filter(|e| e.file_type().is_file()); for icon in icons { - let Some(fname) = icon.file_name().to_str() else { continue }; + let Some(fname) = icon.file_name().to_str() else { + continue; + }; if !is_icon(&fname) { continue; } - let Some((name, _)) = fname.rsplit_once(".") else { continue }; + let Some((name, _)) = fname.rsplit_once(".") else { + continue; + }; map.entry(name.to_owned()) .or_insert_with(|| icon.path().to_owned()); } diff --git a/plugin-desktop/src/main.rs b/plugin-desktop/src/main.rs index ab9cef5..73c1db5 100644 --- a/plugin-desktop/src/main.rs +++ b/plugin-desktop/src/main.rs @@ -1,4 +1,3 @@ -use std::fs::read_to_string; use std::path::PathBuf; use clap::Parser; @@ -50,28 +49,25 @@ fn fix_exec(exec: &str) -> String { } /// Parse XDG Desktop Entry into RMenu Entry -fn parse_desktop(path: &PathBuf, locale: Option<&str>) -> Option { - let bytes = read_to_string(path).ok()?; - let entry = DesktopEntry::decode(&path, &bytes).ok()?; - - // no-display entries should not be shown +fn parse_desktop(path: PathBuf, locales: &[&str]) -> Option { + let entry = DesktopEntry::from_path(path, locales).ok()?; + // hide `NoDisplay` entries if entry.no_display() { return None; } - - // if an entry only is shown on a specific desktop, check whether desktop is set and matches, - // otherwise return None - let de = std::env::var(XDG_CURRENT_DESKTOP_ENV); - if entry - .only_show_in() - .is_some_and(|e| !(de.is_ok() && de.unwrap() == e.to_string())) - { - return None; + // hide entries restricted by `OnlyShowIn` + if let Ok(de) = std::env::var(XDG_CURRENT_DESKTOP_ENV) { + if entry + .only_show_in() + .is_some_and(|only| only.contains(&de.as_str())) + { + return None; + }; } - - let name = entry.name(locale)?.to_string(); + // parse desktop entry into rmenu entry + let name = entry.name(locales)?.to_string(); let icon = entry.icon().map(|i| i.to_string()); - let comment = entry.comment(locale).map(|s| s.to_string()); + let comment = entry.comment(locales).map(|s| s.to_string()); let terminal = entry.terminal(); let mut actions = match entry.exec() { Some(exec) => vec![Action { @@ -84,12 +80,11 @@ fn parse_desktop(path: &PathBuf, locale: Option<&str>) -> Option { actions.extend( entry .actions() - .unwrap_or("") - .split(";") + .unwrap_or_default() .into_iter() .filter(|a| a.len() > 0) .filter_map(|a| { - let name = entry.action_name(a, locale)?; + let name = entry.action_name(a, locales)?; let exec = entry.action_exec(a)?; Some(Action { name: name.to_string(), @@ -124,17 +119,20 @@ struct Cli { /// Only Allow Unique Desktop Entries #[clap(short, long)] non_unique: bool, + /// Locale Override + #[clap(short, long, default_value = "en")] + locale: String, } fn main() { let cli = Cli::parse(); - let locale = Some("en"); + let locales = &[cli.locale.as_str()]; let sizes = vec![64, 32, 96, 22, 128]; // collect icons let cfg = config_dir(); - let spec = icons::IconSpec::standard(&cfg, sizes); - let icons = icons::collect_icons(spec); + let spec = icons::IconSpec::standard(&cfg, sizes, locales); + let icons = icons::collect_icons(spec, locales); // collect applications let app_paths = data_dirs("applications"); @@ -147,7 +145,7 @@ fn main() { .and_then(|n| n.to_str()) .map(|s| s.to_string()), }) - .filter_map(|f| parse_desktop(&f, locale)) + .filter_map(|f| parse_desktop(f, locales)) .map(|mut e| { e.icon = e.icon.and_then(|s| assign_icon(s, &icons)); e