Skip to content

Commit

Permalink
feat: readd entry matching
Browse files Browse the repository at this point in the history
  • Loading branch information
wiiznokes authored Aug 20, 2024
1 parent c509c74 commit ee9a759
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 2 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ categories = [ "os::unix-apis" ]
keywords = [ "freedesktop", "desktop", "entry" ]

[dependencies]
dirs = "5.0.1"
gettext-rs = { version = "0.7", features = ["gettext-system"]}
memchr = "2"
textdistance = "1.0.2"
strsim = "0.11.1"
thiserror = "1"
xdg = "2.4.0"
log = "0.4.21"
11 changes: 9 additions & 2 deletions src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ impl<'a> DesktopEntry<'a> {
}

/// Return an owned [`DesktopEntry`]
pub fn from_path<L>(path: PathBuf, locales_filter: Option<&[L]>) -> Result<DesktopEntry<'static>, DecodeError>
pub fn from_path<L>(
path: PathBuf,
locales_filter: Option<&[L]>,
) -> Result<DesktopEntry<'static>, DecodeError>
where
L: AsRef<str>,
{
Expand Down Expand Up @@ -125,7 +128,11 @@ fn process_line<'buf, 'local_ref, 'res: 'local_ref + 'buf, F, L>(
let locale = &key[start + 1..key.len() - 1];

match locales_filter {
Some(locales_filter) if !locales_filter.iter().any(|l| l.as_ref() == locale) => return,
Some(locales_filter)
if !locales_filter.iter().any(|l| l.as_ref() == locale) =>
{
return
}
_ => (),
}

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
mod decoder;
mod iter;

pub mod matching;
pub use decoder::DecodeError;

pub use self::iter::Iter;
Expand Down
124 changes: 124 additions & 0 deletions src/matching.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright 2021 System76 <[email protected]>
// SPDX-License-Identifier: MPL-2.0

use crate::DesktopEntry;

impl<'a> DesktopEntry<'a> {
/// The returned value is between 0.0 and 1.0 (higher value means more similar).
/// You can use the `additional_haystack_values` parameter to add relevant string that are not part of the desktop entry.
pub fn match_query<Q, L>(
&'a self,
query: Q,
locales: &'a [L],
additional_haystack_values: &'a [&'a str],
) -> f64
where
Q: AsRef<str>,
L: AsRef<str>,
{
#[inline]
fn add_value(v: &mut Vec<String>, value: &str, is_multiple: bool) {
if is_multiple {
value.split(';').for_each(|e| v.push(e.to_lowercase()));
} else {
v.push(value.to_lowercase());
}
}

// (field name, is separated by ";")
let fields = [
("Name", false),
("GenericName", false),
("Comment", false),
("Categories", true),
("Keywords", true),
];

let mut normalized_values: Vec<String> = Vec::new();

normalized_values.extend(
additional_haystack_values
.iter()
.map(|val| val.to_lowercase()),
);

let desktop_entry_group = self.groups.get("Desktop Entry");

for field in fields {
if let Some(group) = desktop_entry_group {
if let Some((default_value, locale_map)) = group.get(field.0) {
add_value(&mut normalized_values, default_value, field.1);

let mut at_least_one_locale = false;

for locale in locales {
match locale_map.get(locale.as_ref()) {
Some(value) => {
add_value(&mut normalized_values, value, field.1);
at_least_one_locale = true;
}
None => {
if let Some(pos) = locale.as_ref().find('_') {
if let Some(value) = locale_map.get(&locale.as_ref()[..pos]) {
add_value(&mut normalized_values, value, field.1);
at_least_one_locale = true;
}
}
}
}
}

if !at_least_one_locale {
if let Some(domain) = &self.ubuntu_gettext_domain {
let gettext_value = crate::dgettext(domain, default_value);
if !gettext_value.is_empty() {
add_value(&mut normalized_values, &gettext_value, false);
}
}
}
}
}
}

let query = query.as_ref().to_lowercase();

let query_espaced = query.split_ascii_whitespace().collect::<Vec<_>>();

normalized_values
.into_iter()
.map(|de_field| {
let jaro_score = strsim::jaro_winkler(&query, &de_field);

if query_espaced.iter().any(|query| de_field.contains(*query)) {
// provide a bonus if the query is contained in the de field
(jaro_score + 0.1).clamp(0.61, 1.)
} else {
jaro_score
}
})
.max_by(|e1, e2| e1.total_cmp(e2))
.unwrap_or(0.0)
}
}

/// Return the corresponding [`DesktopEntry`] that match the given appid.
pub fn find_entry_from_appid<'a, I>(entries: I, appid: &str) -> Option<&'a DesktopEntry<'a>>
where
I: Iterator<Item = &'a DesktopEntry<'a>>,
{
let normalized_appid = appid.to_lowercase();

entries.into_iter().find(|e| {
if e.appid.to_lowercase() == normalized_appid {
return true;
}

if let Some(field) = e.startup_wm_class() {
if field.to_lowercase() == normalized_appid {
return true;
}
}

false
})
}

0 comments on commit ee9a759

Please sign in to comment.