Skip to content

Commit 375da73

Browse files
authored
improv: improvements for cosmic app list applet
1 parent babe815 commit 375da73

File tree

7 files changed

+132
-91
lines changed

7 files changed

+132
-91
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn main() {
1818
for path in Iter::new(default_paths()) {
1919
let path_src = PathSource::guess_from(&path);
2020
if let Ok(bytes) = fs::read_to_string(&path) {
21-
if let Ok(entry) = DesktopEntry::decode_from_str(&path, &bytes, &locales) {
21+
if let Ok(entry) = DesktopEntry::from_str(&path, &bytes, &locales) {
2222
println!("{:?}: {}\n---\n{}", path_src, path.display(), entry);
2323
}
2424
}

examples/all_files.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn main() {
1313
for path in Iter::new(default_paths()) {
1414
let path_src = PathSource::guess_from(&path);
1515
if let Ok(bytes) = fs::read_to_string(&path) {
16-
if let Ok(entry) = DesktopEntry::decode_from_str(&path, &bytes, &locales) {
16+
if let Ok(entry) = DesktopEntry::from_str(&path, &bytes, &locales) {
1717
println!("{:?}: {}\n---\n{}", path_src, path.display(), entry);
1818
}
1919
}

examples/bench.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fn bench_borrowed(it: u32) {
2626

2727
for path in paths {
2828
if let Ok(bytes) = fs::read_to_string(&path) {
29-
if let Ok(_entry) = DesktopEntry::decode_from_str(&path, &bytes, &locale) {}
29+
if let Ok(_entry) = DesktopEntry::from_str(&path, &bytes, &locale) {}
3030
}
3131
}
3232

@@ -46,7 +46,7 @@ fn bench_owned(it: u32) {
4646
let now = Instant::now();
4747

4848
for path in paths {
49-
if let Ok(_entry) = DesktopEntry::decode_from_path(path, &locale) {}
49+
if let Ok(_entry) = DesktopEntry::from_path(path, &locale) {}
5050
}
5151

5252
total_time += now.elapsed();
@@ -64,7 +64,7 @@ fn bench_owned_optimized(it: u32) {
6464

6565
let now = Instant::now();
6666

67-
let _ = DesktopEntry::decode_from_paths(paths, &locale)
67+
let _ = DesktopEntry::from_paths(paths, &locale)
6868
.filter_map(|e| e.ok())
6969
.collect::<Vec<_>>();
7070

examples/specific_file.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ fn main() {
1212
// }
1313
// }
1414

15-
if let Ok(entry) = DesktopEntry::decode_from_path(path.to_path_buf(), locales) {
15+
if let Ok(entry) = DesktopEntry::from_path(path.to_path_buf(), locales) {
1616
println!("{}\n---\n{}", path.display(), entry);
1717
}
1818
}

src/decoder.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub enum DecodeError {
1919
}
2020

2121
impl<'a> DesktopEntry<'a> {
22-
pub fn decode_from_str<L>(
22+
pub fn from_str<L>(
2323
path: &'a Path,
2424
input: &'a str,
2525
locales: &[L],
@@ -54,7 +54,7 @@ impl<'a> DesktopEntry<'a> {
5454
})
5555
}
5656

57-
pub fn decode_from_paths<'i, 'l: 'i, L>(
57+
pub fn from_paths<'i, 'l: 'i, L>(
5858
paths: impl Iterator<Item = PathBuf> + 'i,
5959
locales: &'l [L],
6060
) -> impl Iterator<Item = Result<DesktopEntry<'static>, DecodeError>> + 'i
@@ -68,7 +68,7 @@ impl<'a> DesktopEntry<'a> {
6868
}
6969

7070
/// Return an owned [`DesktopEntry`]
71-
pub fn decode_from_path<L>(
71+
pub fn from_path<L>(
7272
path: PathBuf,
7373
locales: &[L],
7474
) -> Result<DesktopEntry<'static>, DecodeError>

src/lib.rs

Lines changed: 122 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -24,112 +24,76 @@ pub type Locale<'a> = Cow<'a, str>;
2424
pub type LocaleMap<'a> = BTreeMap<Locale<'a>, Value<'a>>;
2525
pub type Value<'a> = Cow<'a, str>;
2626

27-
#[derive(Debug)]
27+
#[derive(Debug, Clone)]
2828
pub struct DesktopEntry<'a> {
2929
pub appid: Cow<'a, str>,
3030
pub groups: Groups<'a>,
3131
pub path: Cow<'a, Path>,
3232
pub ubuntu_gettext_domain: Option<Cow<'a, str>>,
3333
}
3434

35+
impl DesktopEntry<'_> {
36+
/// Construct a new [`DesktopEntry`] from an appid. The name field will be
37+
/// set to that appid.
38+
pub fn from_appid<'a>(appid: &'a str) -> DesktopEntry<'a> {
39+
let mut de = DesktopEntry {
40+
appid: Cow::Borrowed(appid),
41+
groups: Groups::default(),
42+
path: Cow::Owned(PathBuf::from("")),
43+
ubuntu_gettext_domain: None,
44+
};
45+
de.add_desktop_entry("Name", appid);
46+
de
47+
}
48+
}
49+
3550
impl<'a> DesktopEntry<'a> {
36-
pub fn into_owned(self) -> DesktopEntry<'static> {
51+
// note that we shoudn't implement ToOwned in this case: https://stackoverflow.com/questions/72105604/implement-toowned-for-user-defined-types
52+
pub fn to_owned(&self) -> DesktopEntry<'static> {
3753
let mut new_groups = Groups::new();
3854

39-
for (group, key_map) in self.groups {
55+
for (group, key_map) in &self.groups {
4056
let mut new_key_map = KeyMap::new();
4157

4258
for (key, (value, locale_map)) in key_map {
4359
let mut new_locale_map = LocaleMap::new();
4460

4561
for (locale, value) in locale_map {
4662
new_locale_map.insert(
47-
Cow::Owned(locale.into_owned()),
48-
Cow::Owned(value.into_owned()),
63+
Cow::Owned(locale.to_string()),
64+
Cow::Owned(value.to_string()),
4965
);
5066
}
5167

5268
new_key_map.insert(
53-
Cow::Owned(key.into_owned()),
54-
(Cow::Owned(value.into_owned()), new_locale_map),
69+
Cow::Owned(key.to_string()),
70+
(Cow::Owned(value.to_string()), new_locale_map),
5571
);
5672
}
5773

58-
new_groups.insert(Cow::Owned(group.into_owned()), new_key_map);
74+
new_groups.insert(Cow::Owned(group.to_string()), new_key_map);
5975
}
6076

6177
DesktopEntry {
62-
appid: Cow::Owned(self.appid.into_owned()),
78+
appid: Cow::Owned(self.appid.to_string()),
6379
groups: new_groups,
64-
ubuntu_gettext_domain: self
65-
.ubuntu_gettext_domain
66-
.map(|e| Cow::Owned(e.into_owned())),
67-
path: Cow::Owned(self.path.into_owned()),
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+
},
86+
path: Cow::Owned(self.path.to_path_buf()),
6887
}
6988
}
7089
}
7190

7291
impl<'a> DesktopEntry<'a> {
73-
/// An action is defined as `[Desktop Action actions-name]` where `action-name`
74-
/// is defined in the `Actions` field of `[Desktop Entry]`.
75-
/// Example: to get the `Name` field of this `new-window` action
76-
/// ```txt
77-
/// [Desktop Action new-window]
78-
/// Name=Open a New Window
79-
/// ```
80-
/// you will need to call
81-
/// ```rust
82-
/// entry.action_entry("new-window", "Name")
83-
/// ```
84-
pub fn action_entry(&'a self, action: &str, key: &str) -> Option<&'a str> {
85-
let group = self
86-
.groups
87-
.get(["Desktop Action ", action].concat().as_str());
88-
89-
Self::entry(group, key)
90-
}
91-
92-
pub fn action_entry_localized<L: AsRef<str>>(
93-
&'a self,
94-
action: &str,
95-
key: &str,
96-
locales: &[L],
97-
) -> Option<Cow<'a, str>> {
98-
let group = self
99-
.groups
100-
.get(["Desktop Action ", action].concat().as_str());
101-
102-
Self::localized_entry(self.ubuntu_gettext_domain.as_deref(), group, key, locales)
103-
}
104-
105-
pub fn action_exec(&'a self, action: &str) -> Option<&'a str> {
106-
self.action_entry(action, "Exec")
107-
}
108-
109-
pub fn action_name<L: AsRef<str>>(
110-
&'a self,
111-
action: &str,
112-
locales: &[L],
113-
) -> Option<Cow<'a, str>> {
114-
self.action_entry_localized(action, "Name", locales)
115-
}
116-
117-
/// Return actions separated by `;`
118-
pub fn actions(&'a self) -> Option<&'a str> {
119-
self.desktop_entry("Actions")
120-
}
121-
122-
/// Return categories separated by `;`
123-
pub fn categories(&'a self) -> Option<&'a str> {
124-
self.desktop_entry("Categories")
125-
}
126-
127-
pub fn comment<L: AsRef<str>>(&'a self, locales: &[L]) -> Option<Cow<'a, str>> {
128-
self.desktop_entry_localized("Comment", locales)
92+
pub fn id(&'a self) -> &'a str {
93+
self.appid.as_ref()
12994
}
13095

131-
/// A desktop entry field is any field under the
132-
/// `[Desktop Entry]` line
96+
/// A desktop entry field is any field under the `[Desktop Entry]` section.
13397
pub fn desktop_entry(&'a self, key: &str) -> Option<&'a str> {
13498
Self::entry(self.groups.get("Desktop Entry"), key).map(|e| e.as_ref())
13599
}
@@ -147,12 +111,30 @@ impl<'a> DesktopEntry<'a> {
147111
)
148112
}
149113

150-
pub fn exec(&'a self) -> Option<&'a str> {
151-
self.desktop_entry("Exec")
114+
/// Insert a new field to this [`DesktopEntry`], in the `[Desktop Entry]` section, removing
115+
/// the previous value and locales in any.
116+
pub fn add_desktop_entry<'b>(&'b mut self, key: &'a str, value: &'a str)
117+
where
118+
'a: 'b,
119+
{
120+
let action_key = "Desktop Entry";
121+
let key = Cow::Borrowed(key);
122+
let value = (Cow::Borrowed(value), LocaleMap::default());
123+
124+
match self.groups.get_mut(action_key) {
125+
Some(keymap) => {
126+
keymap.insert(key, value);
127+
}
128+
None => {
129+
let mut keymap = KeyMap::default();
130+
keymap.insert(key, value);
131+
self.groups.insert(Cow::Borrowed(action_key), keymap);
132+
}
133+
}
152134
}
153135

154-
pub fn flatpak(&'a self) -> Option<&'a str> {
155-
self.desktop_entry("X-Flatpak")
136+
pub fn name<L: AsRef<str>>(&'a self, locales: &[L]) -> Option<Cow<'a, str>> {
137+
self.desktop_entry_localized("Name", locales)
156138
}
157139

158140
pub fn generic_name<L: AsRef<str>>(&'a self, locales: &[L]) -> Option<Cow<'a, str>> {
@@ -163,8 +145,18 @@ impl<'a> DesktopEntry<'a> {
163145
self.desktop_entry("Icon")
164146
}
165147

166-
pub fn id(&'a self) -> &'a str {
167-
self.appid.as_ref()
148+
/// This is an human readable description of the desktop file.
149+
pub fn comment<L: AsRef<str>>(&'a self, locales: &[L]) -> Option<Cow<'a, str>> {
150+
self.desktop_entry_localized("Comment", locales)
151+
}
152+
153+
pub fn exec(&'a self) -> Option<&'a str> {
154+
self.desktop_entry("Exec")
155+
}
156+
157+
/// Return categories separated by `;`
158+
pub fn categories(&'a self) -> Option<&'a str> {
159+
self.desktop_entry("Categories")
168160
}
169161

170162
/// Return keywords separated by `;`
@@ -177,10 +169,6 @@ impl<'a> DesktopEntry<'a> {
177169
self.desktop_entry("MimeType")
178170
}
179171

180-
pub fn name<L: AsRef<str>>(&'a self, locales: &[L]) -> Option<Cow<'a, str>> {
181-
self.desktop_entry_localized("Name", locales)
182-
}
183-
184172
pub fn no_display(&'a self) -> bool {
185173
self.desktop_entry_bool("NoDisplay")
186174
}
@@ -189,6 +177,10 @@ impl<'a> DesktopEntry<'a> {
189177
self.desktop_entry("OnlyShowIn")
190178
}
191179

180+
pub fn flatpak(&'a self) -> Option<&'a str> {
181+
self.desktop_entry("X-Flatpak")
182+
}
183+
192184
pub fn prefers_non_default_gpu(&'a self) -> bool {
193185
self.desktop_entry_bool("PrefersNonDefaultGPU")
194186
}
@@ -209,6 +201,55 @@ impl<'a> DesktopEntry<'a> {
209201
self.desktop_entry("Type")
210202
}
211203

204+
/// Return actions separated by `;`
205+
pub fn actions(&'a self) -> Option<&'a str> {
206+
self.desktop_entry("Actions")
207+
}
208+
209+
/// An action is defined as `[Desktop Action actions-name]` where `action-name`
210+
/// is defined in the `Actions` field of `[Desktop Entry]`.
211+
/// Example: to get the `Name` field of this `new-window` action
212+
/// ```txt
213+
/// [Desktop Action new-window]
214+
/// Name=Open a New Window
215+
/// ```
216+
/// you will need to call
217+
/// ```rust
218+
/// entry.action_entry("new-window", "Name")
219+
/// ```
220+
pub fn action_entry(&'a self, action: &str, key: &str) -> Option<&'a str> {
221+
let group = self
222+
.groups
223+
.get(["Desktop Action ", action].concat().as_str());
224+
225+
Self::entry(group, key)
226+
}
227+
228+
pub fn action_entry_localized<L: AsRef<str>>(
229+
&'a self,
230+
action: &str,
231+
key: &str,
232+
locales: &[L],
233+
) -> Option<Cow<'a, str>> {
234+
let group = self
235+
.groups
236+
.get(["Desktop Action ", action].concat().as_str());
237+
238+
Self::localized_entry(self.ubuntu_gettext_domain.as_deref(), group, key, locales)
239+
}
240+
241+
pub fn action_name<L: AsRef<str>>(
242+
&'a self,
243+
action: &str,
244+
locales: &[L],
245+
) -> Option<Cow<'a, str>> {
246+
self.action_entry_localized(action, "Name", locales)
247+
}
248+
249+
pub fn action_exec(&'a self, action: &str) -> Option<&'a str> {
250+
self.action_entry(action, "Exec")
251+
}
252+
212253
fn desktop_entry_bool(&'a self, key: &str) -> bool {
213254
self.desktop_entry(key).map_or(false, |v| v == "true")
214255
}

src/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ fn test() {
88

99
let locales = &["fr", "fr_FR.UTF-8"];
1010

11-
if let Ok(entry) = DesktopEntry::decode_from_path(path.to_path_buf(), locales) {
11+
if let Ok(entry) = DesktopEntry::from_path(path.to_path_buf(), locales) {
1212
let e = DesktopEntry::localized_entry(
1313
None,
1414
entry.groups.get("Desktop Entry"),

0 commit comments

Comments
 (0)