desktop: hash entries the same way they compare, only parse *.desktop
Desktop derived Hash over every field but PartialEq compared only the name, violating the Hash/Eq contract the launcher's HashMap relies on (clippy denies this by default, so `cargo clippy` failed to build). Also skip directories and files without a .desktop extension (mimeinfo cache, .directory files, ...) and stop unwrapping non-UTF-8 paths. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
2bed8b7fac
commit
01af00b5ea
+20
-5
@@ -3,7 +3,9 @@ use std::{
|
|||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
error::Error,
|
error::Error,
|
||||||
fs::{read_to_string, File},
|
fs::{read_to_string, File},
|
||||||
|
hash::{Hash, Hasher},
|
||||||
io::{Error as io_error, ErrorKind},
|
io::{Error as io_error, ErrorKind},
|
||||||
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{self, Deserialize, Serialize};
|
use serde::{self, Deserialize, Serialize};
|
||||||
@@ -12,7 +14,7 @@ use walkdir::WalkDir;
|
|||||||
|
|
||||||
use crate::utils::{inish, xdg};
|
use crate::utils::{inish, xdg};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, Hash)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub struct Desktop {
|
pub struct Desktop {
|
||||||
pub entry_type: String,
|
pub entry_type: String,
|
||||||
@@ -26,7 +28,7 @@ pub struct Desktop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Desktop {
|
impl Desktop {
|
||||||
fn parse(f: &str) -> Result<Desktop, Box<dyn Error>> {
|
fn parse(f: &Path) -> Result<Desktop, Box<dyn Error>> {
|
||||||
let s = read_to_string(f)?;
|
let s = read_to_string(f)?;
|
||||||
let config = inish::parse(&s)?;
|
let config = inish::parse(&s)?;
|
||||||
match config.get("Desktop Entry") {
|
match config.get("Desktop Entry") {
|
||||||
@@ -74,6 +76,13 @@ impl PartialEq for Desktop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must agree with PartialEq: equal entries (same name) must hash equally.
|
||||||
|
impl Hash for Desktop {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.name.hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_desktop_cache() -> Result<Vec<Desktop>, Box<dyn Error>> {
|
pub fn load_desktop_cache() -> Result<Vec<Desktop>, Box<dyn Error>> {
|
||||||
match File::open(format!("{}/wldash/desktop.json", xdg::cache_folder())) {
|
match File::open(format!("{}/wldash/desktop.json", xdg::cache_folder())) {
|
||||||
Ok(f) => simd_json::from_reader(f).map_err(|e| e.into()),
|
Ok(f) => simd_json::from_reader(f).map_err(|e| e.into()),
|
||||||
@@ -95,9 +104,15 @@ pub fn load_desktop_files() -> Vec<Desktop> {
|
|||||||
let mut seen: HashSet<String> = HashSet::new();
|
let mut seen: HashSet<String> = HashSet::new();
|
||||||
|
|
||||||
for dir in dirs {
|
for dir in dirs {
|
||||||
for entry in WalkDir::new(format!("{}/applications", dir)) {
|
for entry in WalkDir::new(format!("{}/applications", dir)).into_iter().flatten() {
|
||||||
if let Ok(entry) = entry {
|
// Entries are often symlinks, so only rule out directories.
|
||||||
if let Ok(d) = Desktop::parse(entry.path().to_str().unwrap()) {
|
if entry.file_type().is_dir()
|
||||||
|
|| entry.path().extension().and_then(|e| e.to_str()) != Some("desktop")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
if let Ok(d) = Desktop::parse(entry.path()) {
|
||||||
if d.hidden
|
if d.hidden
|
||||||
|| d.no_display
|
|| d.no_display
|
||||||
|| (d.entry_type != "Application" && d.entry_type != "Link")
|
|| (d.entry_type != "Application" && d.entry_type != "Link")
|
||||||
|
|||||||
Reference in New Issue
Block a user