mirror of
https://github.com/imgurbot12/rmenu.git
synced 2024-11-10 11:33:48 +01:00
feat: desktop2 replace desktop1 plugin, added makefile
This commit is contained in:
parent
48af7cee47
commit
72267fb029
@ -5,5 +5,4 @@ members = [
|
|||||||
"rmenu-plugin",
|
"rmenu-plugin",
|
||||||
"plugin-run",
|
"plugin-run",
|
||||||
"plugin-desktop",
|
"plugin-desktop",
|
||||||
"plugin-desktop2",
|
|
||||||
]
|
]
|
||||||
|
24
Makefile
Normal file
24
Makefile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# RMenu Installation/Deployment Configuration
|
||||||
|
|
||||||
|
CARGO=cargo
|
||||||
|
FLAGS=--release
|
||||||
|
|
||||||
|
DEST=$(HOME)/.config/rmenu
|
||||||
|
|
||||||
|
install: build deploy
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
mkdir -p ${DEST}
|
||||||
|
cp -vf ./target/release/desktop ${DEST}/drun
|
||||||
|
cp -vf ./target/release/run ${DEST}/run
|
||||||
|
cp -vf ./rmenu/public/config.yaml ${DEST}/config.yaml
|
||||||
|
cp -vf ./rmenu/public/default.css ${DEST}/style.css
|
||||||
|
|
||||||
|
build: build-rmenu build-plugins
|
||||||
|
|
||||||
|
build-rmenu:
|
||||||
|
${CARGO} build -p rmenu ${FLAGS}
|
||||||
|
|
||||||
|
build-plugins:
|
||||||
|
${CARGO} build -p run ${FLAGS}
|
||||||
|
${CARGO} build -p desktop ${FLAGS}
|
@ -7,9 +7,13 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
freedesktop-desktop-entry = "0.5.0"
|
freedesktop-desktop-entry = "0.5.0"
|
||||||
|
freedesktop-icons = "0.2.3"
|
||||||
|
log = "0.4.19"
|
||||||
|
once_cell = "1.18.0"
|
||||||
regex = "1.9.1"
|
regex = "1.9.1"
|
||||||
rmenu-plugin = { version = "0.0.0", path = "../rmenu-plugin" }
|
rmenu-plugin = { version = "0.0.0", path = "../rmenu-plugin" }
|
||||||
rust-ini = { version = "0.19.0", features = ["unicase"] }
|
rust-ini = "0.19.0"
|
||||||
serde_json = "1.0.103"
|
serde_json = "1.0.104"
|
||||||
shellexpand = "3.1.0"
|
shellexpand = "3.1.0"
|
||||||
|
thiserror = "1.0.44"
|
||||||
walkdir = "2.3.3"
|
walkdir = "2.3.3"
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::fs::read_to_string;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{fs::read_to_string, path::Path};
|
|
||||||
|
|
||||||
use freedesktop_desktop_entry::DesktopEntry;
|
use freedesktop_desktop_entry::{DesktopEntry, Iter};
|
||||||
use ini::Ini;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rmenu_plugin::{Action, Entry, Method};
|
use rmenu_plugin::{Action, Entry, Method};
|
||||||
use walkdir::WalkDir;
|
|
||||||
|
mod icons;
|
||||||
|
|
||||||
static XDG_DATA_ENV: &'static str = "XDG_DATA_DIRS";
|
static XDG_DATA_ENV: &'static str = "XDG_DATA_DIRS";
|
||||||
static XDG_CONFIG_ENV: &'static str = "XDG_CONFIG_HOME";
|
static XDG_CONFIG_ENV: &'static str = "XDG_CONFIG_HOME";
|
||||||
static XDG_DATA_DEFAULT: &'static str = "/usr/share:/usr/local/share";
|
static XDG_DATA_DEFAULT: &'static str = "/usr/share:/usr/local/share";
|
||||||
static XDG_CONFIG_DEFAULT: &'static str = "~/.config";
|
static XDG_CONFIG_DEFAULT: &'static str = "~/.config";
|
||||||
static DEFAULT_THEME: &'static str = "hicolor";
|
|
||||||
|
static EXEC_RGX: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(r"%\w").expect("Failed to Build Exec Regex"));
|
||||||
|
|
||||||
/// Retrieve XDG-CONFIG-HOME Directory
|
/// Retrieve XDG-CONFIG-HOME Directory
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -21,140 +23,6 @@ fn config_dir() -> PathBuf {
|
|||||||
PathBuf::from(shellexpand::tilde(&path).to_string())
|
PathBuf::from(shellexpand::tilde(&path).to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine XDG Icon Theme based on Preexisting Configuration Files
|
|
||||||
fn find_theme(cfgdir: &PathBuf) -> Vec<String> {
|
|
||||||
let mut themes: Vec<String> = vec![
|
|
||||||
("kdeglobals", "Icons", "Theme"),
|
|
||||||
("gtk-4.0/settings.ini", "Settings", "gtk-icon-theme-name"),
|
|
||||||
("gtk-3.0/settings.ini", "Settings", "gtk-icon-theme-name"),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|(path, sec, key)| {
|
|
||||||
let path = cfgdir.join(path);
|
|
||||||
let ini = Ini::load_from_file(path).ok()?;
|
|
||||||
ini.get_from(Some(sec), key).map(|s| s.to_string())
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let default = DEFAULT_THEME.to_string();
|
|
||||||
if !themes.contains(&default) {
|
|
||||||
themes.push(default);
|
|
||||||
}
|
|
||||||
themes
|
|
||||||
}
|
|
||||||
|
|
||||||
type IconGroup = HashMap<String, PathBuf>;
|
|
||||||
type Icons = HashMap<String, IconGroup>;
|
|
||||||
|
|
||||||
/// Precalculate prefferred sizes folders
|
|
||||||
fn calculate_sizes(range: (usize, usize, usize)) -> HashSet<String> {
|
|
||||||
let (min, preffered, max) = range;
|
|
||||||
let mut size = preffered.clone();
|
|
||||||
let mut sizes = HashSet::new();
|
|
||||||
while size < max {
|
|
||||||
sizes.insert(format!("{size}x{size}"));
|
|
||||||
sizes.insert(format!("{size}x{size}@2"));
|
|
||||||
size *= 2;
|
|
||||||
}
|
|
||||||
// attempt to match sizes down to lowest minimum
|
|
||||||
let mut size = preffered.clone();
|
|
||||||
while size > min {
|
|
||||||
sizes.insert(format!("{size}x{size}"));
|
|
||||||
sizes.insert(format!("{size}x{size}@2"));
|
|
||||||
size /= 2;
|
|
||||||
}
|
|
||||||
sizes
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn is_valid_icon(name: &str) -> bool {
|
|
||||||
name.ends_with(".png") || name.ends_with(".svg")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse Icon-Name from Filename
|
|
||||||
#[inline]
|
|
||||||
fn icon_name(name: &str) -> String {
|
|
||||||
name.rsplit_once(".")
|
|
||||||
.map(|(i, _)| i)
|
|
||||||
.unwrap_or(&name)
|
|
||||||
.to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse and Categorize Icons Within the Specified Path
|
|
||||||
fn find_icons(path: &PathBuf, sizes: (usize, usize, usize)) -> Vec<IconGroup> {
|
|
||||||
let sizes = calculate_sizes(sizes);
|
|
||||||
let mut extras = IconGroup::new();
|
|
||||||
let icons: Icons = WalkDir::new(path)
|
|
||||||
// collect list of directories of icon subdirs
|
|
||||||
.max_depth(1)
|
|
||||||
.follow_links(true)
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|e| e.ok())
|
|
||||||
.filter_map(|e| {
|
|
||||||
let name = e.file_name().to_str()?;
|
|
||||||
let path = e.path().to_owned();
|
|
||||||
match e.file_type().is_dir() {
|
|
||||||
true => Some((name.to_owned(), path)),
|
|
||||||
false => {
|
|
||||||
if is_valid_icon(name) {
|
|
||||||
extras.insert(icon_name(name), path);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// iterate content within subdirs
|
|
||||||
.map(|(name, path)| {
|
|
||||||
let group = WalkDir::new(path)
|
|
||||||
.follow_links(true)
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|e| e.ok())
|
|
||||||
.filter(|e| e.file_type().is_file())
|
|
||||||
.filter_map(|e| {
|
|
||||||
let name = e.file_name().to_str()?;
|
|
||||||
if is_valid_icon(name) {
|
|
||||||
return Some((icon_name(name), e.path().to_owned()));
|
|
||||||
}
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
(name, group)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
// organize icon groups according to prefference
|
|
||||||
let mut priority = vec![];
|
|
||||||
let mut others = vec![];
|
|
||||||
icons
|
|
||||||
.into_iter()
|
|
||||||
.map(|(folder, group)| match sizes.contains(&folder) {
|
|
||||||
true => priority.push(group),
|
|
||||||
false => match folder.contains("x") {
|
|
||||||
false => others.push(group),
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.last();
|
|
||||||
priority.append(&mut others);
|
|
||||||
priority.push(extras);
|
|
||||||
priority
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve Extras in Base Icon Directories
|
|
||||||
fn find_icon_extras(path: &PathBuf) -> IconGroup {
|
|
||||||
WalkDir::new(path)
|
|
||||||
.max_depth(1)
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|e| e.ok())
|
|
||||||
.filter(|e| e.file_type().is_file())
|
|
||||||
.filter_map(|e| {
|
|
||||||
let name = e.file_name().to_str()?;
|
|
||||||
if is_valid_icon(name) {
|
|
||||||
return Some((icon_name(&name), e.path().to_owned()));
|
|
||||||
}
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve XDG-DATA Directories
|
/// Retrieve XDG-DATA Directories
|
||||||
fn data_dirs(dir: &str) -> Vec<PathBuf> {
|
fn data_dirs(dir: &str) -> Vec<PathBuf> {
|
||||||
std::env::var(XDG_DATA_ENV)
|
std::env::var(XDG_DATA_ENV)
|
||||||
@ -167,23 +35,24 @@ fn data_dirs(dir: &str) -> Vec<PathBuf> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Modify Exec Statements to Remove %u/%f/etc...
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn fix_exec(exec: &str, ematch: &Regex) -> String {
|
fn fix_exec(exec: &str) -> String {
|
||||||
ematch.replace_all(exec, "").trim().to_string()
|
EXEC_RGX.replace_all(exec, "").trim().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse XDG Desktop Entry into RMenu Entry
|
/// Parse XDG Desktop Entry into RMenu Entry
|
||||||
fn parse_desktop(path: &Path, locale: Option<&str>, ematch: &Regex) -> Option<Entry> {
|
fn parse_desktop(path: &PathBuf, locale: Option<&str>) -> Option<Entry> {
|
||||||
let bytes = read_to_string(path).ok()?;
|
let bytes = read_to_string(path).ok()?;
|
||||||
let entry = DesktopEntry::decode(&path, &bytes).ok()?;
|
let entry = DesktopEntry::decode(&path, &bytes).ok()?;
|
||||||
let name = entry.name(locale)?.to_string();
|
let name = entry.name(locale)?.to_string();
|
||||||
let icon = entry.icon().map(|s| s.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(locale).map(|s| s.to_string());
|
||||||
let terminal = entry.terminal();
|
let terminal = entry.terminal();
|
||||||
let mut actions = match entry.exec() {
|
let mut actions = match entry.exec() {
|
||||||
Some(exec) => vec![Action {
|
Some(exec) => vec![Action {
|
||||||
name: "main".to_string(),
|
name: "main".to_string(),
|
||||||
exec: Method::new(fix_exec(exec, ematch), terminal),
|
exec: Method::new(fix_exec(exec), terminal),
|
||||||
comment: None,
|
comment: None,
|
||||||
}],
|
}],
|
||||||
None => vec![],
|
None => vec![],
|
||||||
@ -200,7 +69,7 @@ fn parse_desktop(path: &Path, locale: Option<&str>, ematch: &Regex) -> Option<En
|
|||||||
let exec = entry.action_exec(a)?;
|
let exec = entry.action_exec(a)?;
|
||||||
Some(Action {
|
Some(Action {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
exec: Method::new(fix_exec(exec, ematch), terminal),
|
exec: Method::new(fix_exec(exec), terminal),
|
||||||
comment: None,
|
comment: None,
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
@ -213,64 +82,41 @@ fn parse_desktop(path: &Path, locale: Option<&str>, ematch: &Regex) -> Option<En
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate Path and Parse All `.desktop` files into Entries
|
/// Assign XDG Icon based on Desktop-Entry
|
||||||
fn find_desktops(path: PathBuf, locale: Option<&str>, ematch: &Regex) -> Vec<Entry> {
|
fn assign_icon(icon: String, map: &icons::IconMap) -> Option<String> {
|
||||||
WalkDir::new(path)
|
if !icon.contains("/") {
|
||||||
.follow_links(true)
|
if let Some(icon) = map.get(&icon) {
|
||||||
.into_iter()
|
if let Some(path) = icon.to_str() {
|
||||||
.filter_map(|e| e.ok())
|
return Some(path.to_owned());
|
||||||
.filter(|e| e.file_name().to_string_lossy().ends_with(".desktop"))
|
|
||||||
.filter(|e| e.file_type().is_file())
|
|
||||||
.filter_map(|e| parse_desktop(e.path(), locale, ematch))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find and Assign Icons from Icon-Cache when Possible
|
|
||||||
fn assign_icons(icons: &Vec<IconGroup>, mut e: Entry) -> Entry {
|
|
||||||
if let Some(name) = e.icon.as_ref() {
|
|
||||||
if !name.contains("/") {
|
|
||||||
if let Some(path) = icons.iter().find_map(|i| i.get(name)) {
|
|
||||||
if let Some(fpath) = path.to_str() {
|
|
||||||
e.icon = Some(fpath.to_owned());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e
|
Some(icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let locale = Some("en");
|
let locale = Some("en");
|
||||||
let sizes = (32, 64, 128);
|
let sizes = vec![64, 32, 96, 22, 128];
|
||||||
// build regex desktop formatter args
|
|
||||||
let ematch = regex::Regex::new(r"%\w").expect("Failed Regex Compile");
|
// collect icons
|
||||||
// build a collection of icons for configured themes
|
let cfg = config_dir();
|
||||||
let cfgdir = config_dir();
|
let spec = icons::IconSpec::standard(&cfg, sizes);
|
||||||
let themes = find_theme(&cfgdir);
|
let icons = icons::collect_icons(spec);
|
||||||
let icon_paths = data_dirs("icons");
|
|
||||||
let mut icons: Vec<IconGroup> = icon_paths
|
// collect applications
|
||||||
// generate list of icon-paths that exist
|
let app_paths = data_dirs("applications");
|
||||||
.iter()
|
let mut desktops: Vec<Entry> = Iter::new(app_paths)
|
||||||
.map(|d| themes.iter().map(|t| d.join(t)))
|
|
||||||
.flatten()
|
|
||||||
.filter(|t| t.exists())
|
|
||||||
// append icon-paths within supported themes
|
|
||||||
.map(|t| find_icons(&t, sizes))
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
// add extra icons found in base folders
|
|
||||||
icons.extend(icon_paths.iter().map(|p| find_icon_extras(p)));
|
|
||||||
// retrieve desktop applications and sort alphabetically
|
|
||||||
let mut applications: Vec<Entry> = data_dirs("applications")
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| find_desktops(p, locale, &ematch))
|
.filter_map(|f| parse_desktop(&f, locale))
|
||||||
.flatten()
|
.map(|mut e| {
|
||||||
|
e.icon = e.icon.and_then(|s| assign_icon(s, &icons));
|
||||||
|
e
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
applications.sort_by_cached_key(|e| e.name.to_owned());
|
|
||||||
// assign icons and print results
|
desktops.sort_by_cached_key(|e| e.name.to_owned());
|
||||||
applications
|
desktops
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|e| assign_icons(&icons, e))
|
|
||||||
.filter_map(|e| serde_json::to_string(&e).ok())
|
.filter_map(|e| serde_json::to_string(&e).ok())
|
||||||
.map(|s| println!("{}", s))
|
.map(|s| println!("{}", s))
|
||||||
.last();
|
.last();
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "desktop2"
|
|
||||||
version = "0.0.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
freedesktop-desktop-entry = "0.5.0"
|
|
||||||
freedesktop-icons = "0.2.3"
|
|
||||||
log = "0.4.19"
|
|
||||||
once_cell = "1.18.0"
|
|
||||||
regex = "1.9.1"
|
|
||||||
rmenu-plugin = { version = "0.0.0", path = "../rmenu-plugin" }
|
|
||||||
rust-ini = "0.19.0"
|
|
||||||
serde_json = "1.0.104"
|
|
||||||
shellexpand = "3.1.0"
|
|
||||||
thiserror = "1.0.44"
|
|
||||||
walkdir = "2.3.3"
|
|
@ -1,123 +0,0 @@
|
|||||||
use std::fs::read_to_string;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use freedesktop_desktop_entry::{DesktopEntry, Iter};
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use regex::Regex;
|
|
||||||
use rmenu_plugin::{Action, Entry, Method};
|
|
||||||
|
|
||||||
mod icons;
|
|
||||||
|
|
||||||
static XDG_DATA_ENV: &'static str = "XDG_DATA_DIRS";
|
|
||||||
static XDG_CONFIG_ENV: &'static str = "XDG_CONFIG_HOME";
|
|
||||||
static XDG_DATA_DEFAULT: &'static str = "/usr/share:/usr/local/share";
|
|
||||||
static XDG_CONFIG_DEFAULT: &'static str = "~/.config";
|
|
||||||
|
|
||||||
static EXEC_RGX: Lazy<Regex> =
|
|
||||||
Lazy::new(|| Regex::new(r"%\w").expect("Failed to Build Exec Regex"));
|
|
||||||
|
|
||||||
/// Retrieve XDG-CONFIG-HOME Directory
|
|
||||||
#[inline]
|
|
||||||
fn config_dir() -> PathBuf {
|
|
||||||
let path = std::env::var(XDG_CONFIG_ENV).unwrap_or_else(|_| XDG_CONFIG_DEFAULT.to_string());
|
|
||||||
PathBuf::from(shellexpand::tilde(&path).to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve XDG-DATA Directories
|
|
||||||
fn data_dirs(dir: &str) -> Vec<PathBuf> {
|
|
||||||
std::env::var(XDG_DATA_ENV)
|
|
||||||
.unwrap_or_else(|_| XDG_DATA_DEFAULT.to_string())
|
|
||||||
.split(":")
|
|
||||||
.map(|p| shellexpand::tilde(p).to_string())
|
|
||||||
.map(PathBuf::from)
|
|
||||||
.map(|p| p.join(dir.to_owned()))
|
|
||||||
.filter(|p| p.exists())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Modify Exec Statements to Remove %u/%f/etc...
|
|
||||||
#[inline(always)]
|
|
||||||
fn fix_exec(exec: &str) -> String {
|
|
||||||
EXEC_RGX.replace_all(exec, "").trim().to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse XDG Desktop Entry into RMenu Entry
|
|
||||||
fn parse_desktop(path: &PathBuf, locale: Option<&str>) -> Option<Entry> {
|
|
||||||
let bytes = read_to_string(path).ok()?;
|
|
||||||
let entry = DesktopEntry::decode(&path, &bytes).ok()?;
|
|
||||||
let name = entry.name(locale)?.to_string();
|
|
||||||
let icon = entry.icon().map(|i| i.to_string());
|
|
||||||
let comment = entry.comment(locale).map(|s| s.to_string());
|
|
||||||
let terminal = entry.terminal();
|
|
||||||
let mut actions = match entry.exec() {
|
|
||||||
Some(exec) => vec![Action {
|
|
||||||
name: "main".to_string(),
|
|
||||||
exec: Method::new(fix_exec(exec), terminal),
|
|
||||||
comment: None,
|
|
||||||
}],
|
|
||||||
None => vec![],
|
|
||||||
};
|
|
||||||
actions.extend(
|
|
||||||
entry
|
|
||||||
.actions()
|
|
||||||
.unwrap_or("")
|
|
||||||
.split(";")
|
|
||||||
.into_iter()
|
|
||||||
.filter(|a| a.len() > 0)
|
|
||||||
.filter_map(|a| {
|
|
||||||
let name = entry.action_name(a, locale)?;
|
|
||||||
let exec = entry.action_exec(a)?;
|
|
||||||
Some(Action {
|
|
||||||
name: name.to_string(),
|
|
||||||
exec: Method::new(fix_exec(exec), terminal),
|
|
||||||
comment: None,
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
Some(Entry {
|
|
||||||
name,
|
|
||||||
actions,
|
|
||||||
comment,
|
|
||||||
icon,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assign XDG Icon based on Desktop-Entry
|
|
||||||
fn assign_icon(icon: String, map: &icons::IconMap) -> Option<String> {
|
|
||||||
if !icon.contains("/") {
|
|
||||||
if let Some(icon) = map.get(&icon) {
|
|
||||||
if let Some(path) = icon.to_str() {
|
|
||||||
return Some(path.to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(icon)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let locale = Some("en");
|
|
||||||
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);
|
|
||||||
|
|
||||||
// collect applications
|
|
||||||
let app_paths = data_dirs("applications");
|
|
||||||
let mut desktops: Vec<Entry> = Iter::new(app_paths)
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|f| parse_desktop(&f, locale))
|
|
||||||
.map(|mut e| {
|
|
||||||
e.icon = e.icon.and_then(|s| assign_icon(s, &icons));
|
|
||||||
e
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
desktops.sort_by_cached_key(|e| e.name.to_owned());
|
|
||||||
desktops
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|e| serde_json::to_string(&e).ok())
|
|
||||||
.map(|s| println!("{}", s))
|
|
||||||
.last();
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user