Skip to content

Commit a05abe5

Browse files
authored
fix: better desktop entry matches, and fix for firefox icon
1 parent 375da73 commit a05abe5

File tree

7 files changed

+386
-96
lines changed

7 files changed

+386
-96
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "freedesktop-desktop-entry"
3-
version = "0.5.4"
3+
version = "0.6.0"
44
authors = ["Michael Aaron Murphy <[email protected]>"]
55
edition = "2021"
66
homepage = "https://github.com/pop-os/freedesktop-desktop-entry"
@@ -18,3 +18,4 @@ textdistance = "1.0.2"
1818
strsim = "0.11.1"
1919
thiserror = "1"
2020
xdg = "2.4.0"
21+
log = "0.4.21"

src/decoder.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Copyright 2021 System76 <[email protected]>
2+
// SPDX-License-Identifier: MPL-2.0
3+
14
use std::{
25
borrow::Cow,
36
collections::BTreeMap,
@@ -68,10 +71,7 @@ impl<'a> DesktopEntry<'a> {
6871
}
6972

7073
/// Return an owned [`DesktopEntry`]
71-
pub fn from_path<L>(
72-
path: PathBuf,
73-
locales: &[L],
74-
) -> Result<DesktopEntry<'static>, DecodeError>
74+
pub fn from_path<L>(path: PathBuf, locales: &[L]) -> Result<DesktopEntry<'static>, DecodeError>
7575
where
7676
L: AsRef<str>,
7777
{
@@ -194,7 +194,7 @@ where
194194
}
195195

196196
/// Ex: if a locale equal fr_FR, add fr
197-
fn add_generic_locales<'a, L: AsRef<str>>(locales: &'a [L]) -> Vec<&'a str> {
197+
fn add_generic_locales<L: AsRef<str>>(locales: &[L]) -> Vec<&str> {
198198
let mut v = Vec::with_capacity(locales.len() + 1);
199199

200200
for l in locales {

src/iter.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ impl Iterator for Iter {
3737
Err(_) => continue,
3838
}
3939
}
40-
4140

4241
return None;
4342
}

src/lib.rs

Lines changed: 109 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ mod iter;
66

77
pub mod matching;
88

9-
#[cfg(test)]
10-
mod test;
11-
129
pub use self::iter::Iter;
1310
use std::borrow::Cow;
1411
use std::collections::BTreeMap;
12+
use std::hash::{Hash, Hasher};
1513

1614
use std::path::{Path, PathBuf};
1715
use xdg::BaseDirectories;
@@ -24,18 +22,30 @@ pub type Locale<'a> = Cow<'a, str>;
2422
pub type LocaleMap<'a> = BTreeMap<Locale<'a>, Value<'a>>;
2523
pub type Value<'a> = Cow<'a, str>;
2624

27-
#[derive(Debug, Clone)]
25+
#[derive(Debug, Clone, Eq)]
2826
pub struct DesktopEntry<'a> {
2927
pub appid: Cow<'a, str>,
3028
pub groups: Groups<'a>,
3129
pub path: Cow<'a, Path>,
3230
pub ubuntu_gettext_domain: Option<Cow<'a, str>>,
3331
}
3432

33+
impl Hash for DesktopEntry<'_> {
34+
fn hash<H: Hasher>(&self, state: &mut H) {
35+
self.appid.hash(state);
36+
}
37+
}
38+
39+
impl PartialEq for DesktopEntry<'_> {
40+
fn eq(&self, other: &Self) -> bool {
41+
self.appid == other.appid
42+
}
43+
}
44+
3545
impl DesktopEntry<'_> {
3646
/// Construct a new [`DesktopEntry`] from an appid. The name field will be
3747
/// set to that appid.
38-
pub fn from_appid<'a>(appid: &'a str) -> DesktopEntry<'a> {
48+
pub fn from_appid(appid: &str) -> DesktopEntry<'_> {
3949
let mut de = DesktopEntry {
4050
appid: Cow::Borrowed(appid),
4151
groups: Groups::default(),
@@ -77,12 +87,10 @@ impl<'a> DesktopEntry<'a> {
7787
DesktopEntry {
7888
appid: Cow::Owned(self.appid.to_string()),
7989
groups: new_groups,
80-
ubuntu_gettext_domain: if let Some(ubuntu_gettext_domain) = &self.ubuntu_gettext_domain
81-
{
82-
Some(Cow::Owned(ubuntu_gettext_domain.to_string()))
83-
} else {
84-
None
85-
},
90+
ubuntu_gettext_domain: self
91+
.ubuntu_gettext_domain
92+
.as_ref()
93+
.map(|ubuntu_gettext_domain| Cow::Owned(ubuntu_gettext_domain.to_string())),
8694
path: Cow::Owned(self.path.to_path_buf()),
8795
}
8896
}
@@ -93,9 +101,9 @@ impl<'a> DesktopEntry<'a> {
93101
self.appid.as_ref()
94102
}
95103

96-
/// A desktop entry field is any field under the `[Desktop Entry]` section.
104+
/// A desktop entry field if any field under the `[Desktop Entry]` section.
97105
pub fn desktop_entry(&'a self, key: &str) -> Option<&'a str> {
98-
Self::entry(self.groups.get("Desktop Entry"), key).map(|e| e.as_ref())
106+
Self::entry(self.groups.get("Desktop Entry"), key)
99107
}
100108

101109
pub fn desktop_entry_localized<L: AsRef<str>>(
@@ -154,27 +162,35 @@ impl<'a> DesktopEntry<'a> {
154162
self.desktop_entry("Exec")
155163
}
156164

157-
/// Return categories separated by `;`
158-
pub fn categories(&'a self) -> Option<&'a str> {
165+
/// Return categories
166+
pub fn categories(&'a self) -> Option<Vec<&'a str>> {
159167
self.desktop_entry("Categories")
168+
.map(|e| e.split(';').collect())
160169
}
161170

162-
/// Return keywords separated by `;`
163-
pub fn keywords<L: AsRef<str>>(&'a self, locales: &[L]) -> Option<Cow<'a, str>> {
164-
self.desktop_entry_localized("Keywords", locales)
171+
/// Return keywords
172+
pub fn keywords<L: AsRef<str>>(&'a self, locales: &[L]) -> Option<Vec<Cow<'a, str>>> {
173+
self.localized_entry_splitted(self.groups.get("Desktop Entry"), "Keywords", locales)
165174
}
166175

167-
/// Return mime types separated by `;`
168-
pub fn mime_type(&'a self) -> Option<&'a str> {
176+
/// Return mime types
177+
pub fn mime_type(&'a self) -> Option<Vec<&'a str>> {
169178
self.desktop_entry("MimeType")
179+
.map(|e| e.split(';').collect())
170180
}
171181

172182
pub fn no_display(&'a self) -> bool {
173183
self.desktop_entry_bool("NoDisplay")
174184
}
175185

176-
pub fn only_show_in(&'a self) -> Option<&'a str> {
186+
pub fn only_show_in(&'a self) -> Option<Vec<&'a str>> {
177187
self.desktop_entry("OnlyShowIn")
188+
.map(|e| e.split(';').collect())
189+
}
190+
191+
pub fn not_show_in(&'a self) -> Option<Vec<&'a str>> {
192+
self.desktop_entry("NotShowIn")
193+
.map(|e| e.split(';').collect())
178194
}
179195

180196
pub fn flatpak(&'a self) -> Option<&'a str> {
@@ -201,9 +217,9 @@ impl<'a> DesktopEntry<'a> {
201217
self.desktop_entry("Type")
202218
}
203219

204-
/// Return actions separated by `;`
205-
pub fn actions(&'a self) -> Option<&'a str> {
220+
pub fn actions(&'a self) -> Option<Vec<&'a str>> {
206221
self.desktop_entry("Actions")
222+
.map(|e| e.split(';').collect())
207223
}
208224

209225
/// An action is defined as `[Desktop Action actions-name]` where `action-name`
@@ -214,7 +230,7 @@ impl<'a> DesktopEntry<'a> {
214230
/// Name=Open a New Window
215231
/// ```
216232
/// you will need to call
217-
/// ```rust
233+
/// ```ignore
218234
/// entry.action_entry("new-window", "Name")
219235
/// ```
220236
pub fn action_entry(&'a self, action: &str, key: &str) -> Option<&'a str> {
@@ -266,13 +282,7 @@ impl<'a> DesktopEntry<'a> {
266282
key: &str,
267283
locales: &[L],
268284
) -> Option<Cow<'a, str>> {
269-
let Some(group) = group else {
270-
return None;
271-
};
272-
273-
let Some((default_value, locale_map)) = group.get(key) else {
274-
return None;
275-
};
285+
let (default_value, locale_map) = group?.get(key)?;
276286

277287
for locale in locales {
278288
match locale_map.get(locale.as_ref()) {
@@ -287,9 +297,43 @@ impl<'a> DesktopEntry<'a> {
287297
}
288298
}
289299
if let Some(domain) = ubuntu_gettext_domain {
290-
return Some(Cow::Owned(dgettext(domain, &default_value)));
300+
return Some(Cow::Owned(dgettext(domain, default_value)));
301+
}
302+
Some(default_value.clone())
303+
}
304+
305+
pub fn localized_entry_splitted<L: AsRef<str>>(
306+
&self,
307+
group: Option<&'a KeyMap<'a>>,
308+
key: &str,
309+
locales: &[L],
310+
) -> Option<Vec<Cow<'a, str>>> {
311+
let (default_value, locale_map) = group?.get(key)?;
312+
313+
for locale in locales {
314+
match locale_map.get(locale.as_ref()) {
315+
Some(value) => {
316+
return Some(value.split(';').map(Cow::Borrowed).collect());
317+
}
318+
None => {
319+
if let Some(pos) = memchr::memchr(b'_', locale.as_ref().as_bytes()) {
320+
if let Some(value) = locale_map.get(&locale.as_ref()[..pos]) {
321+
return Some(value.split(';').map(Cow::Borrowed).collect());
322+
}
323+
}
324+
}
325+
}
326+
}
327+
if let Some(domain) = &self.ubuntu_gettext_domain {
328+
return Some(
329+
dgettext(domain, default_value)
330+
.split(';')
331+
.map(|e| Cow::Owned(e.to_string()))
332+
.collect(),
333+
);
291334
}
292-
return Some(default_value.clone());
335+
336+
Some(default_value.split(';').map(Cow::Borrowed).collect())
293337
}
294338
}
295339

@@ -403,9 +447,40 @@ pub fn get_languages_from_env() -> Vec<String> {
403447
l
404448
}
405449

450+
pub fn current_desktop() -> Option<Vec<String>> {
451+
std::env::var("XDG_CURRENT_DESKTOP").ok().map(|x| {
452+
let x = x.to_ascii_lowercase();
453+
if x == "unity" {
454+
vec!["gnome".to_string()]
455+
} else {
456+
x.split(':').map(|e| e.to_string()).collect()
457+
}
458+
})
459+
}
460+
406461
#[test]
407-
fn locales_env_test() {
462+
fn add_field() {
463+
let appid = "appid";
464+
let de = DesktopEntry::from_appid(appid);
465+
466+
assert_eq!(de.appid, appid);
467+
assert_eq!(de.name(&[] as &[&str]).unwrap(), appid);
468+
408469
let s = get_languages_from_env();
409470

410471
println!("{:?}", s);
411472
}
473+
474+
#[test]
475+
fn env_with_locale() {
476+
let locales = &["fr_FR"];
477+
478+
let de = DesktopEntry::from_path(PathBuf::from("tests/org.mozilla.firefox.desktop"), locales)
479+
.unwrap();
480+
481+
assert_eq!(de.generic_name(locales).unwrap(), "Navigateur Web");
482+
483+
let locales = &["nb"];
484+
485+
assert_eq!(de.generic_name(locales).unwrap(), "Web Browser");
486+
}

0 commit comments

Comments
 (0)