style: apply rustfmt across workspace
Some checks failed
CI / test (pull_request) Failing after 5m30s
CI / clippy (pull_request) Failing after 5m2s
CI / fmt (pull_request) Successful in 26s

This commit is contained in:
2026-03-15 20:02:12 +01:00
parent 1e233aba4b
commit 2feb3a2d96
15 changed files with 493 additions and 109 deletions

View File

@@ -24,7 +24,10 @@ impl FrecencyStore {
.ok()
.and_then(|s| serde_json::from_str(&s).ok())
.unwrap_or_default();
Arc::new(Self { path, data: Mutex::new(data) })
Arc::new(Self {
path,
data: Mutex::new(data),
})
}
#[cfg(test)]
@@ -53,7 +56,10 @@ impl FrecencyStore {
.unwrap_or_default()
.as_secs();
let mut data = self.data.lock().unwrap();
let entry = data.entry(id.to_string()).or_insert(Entry { count: 0, last_used: 0 });
let entry = data.entry(id.to_string()).or_insert(Entry {
count: 0,
last_used: 0,
});
entry.count += 1;
entry.last_used = now;
if let Some(parent) = self.path.parent() {
@@ -72,7 +78,13 @@ impl FrecencyStore {
.unwrap_or_default()
.as_secs();
let age_secs = now.saturating_sub(entry.last_used);
let decay = if age_secs < 3600 { 4 } else if age_secs < 86400 { 2 } else { 1 };
let decay = if age_secs < 3600 {
4
} else if age_secs < 86400 {
2
} else {
1
};
entry.count * decay
}
@@ -86,7 +98,13 @@ impl FrecencyStore {
.iter()
.map(|(id, entry)| {
let age_secs = now.saturating_sub(entry.last_used);
let decay = if age_secs < 3600 { 4 } else if age_secs < 86400 { 2 } else { 1 };
let decay = if age_secs < 3600 {
4
} else if age_secs < 86400 {
2
} else {
1
};
(id.clone(), entry.count * decay)
})
.collect();

View File

@@ -91,7 +91,10 @@ impl AppsPlugin {
let id = format!("app-{}", e.name.as_str());
let keywords_lc = e.keywords.iter().map(|k| k.to_lowercase()).collect();
#[cfg(target_os = "linux")]
let icon = e.icon.as_ref().and_then(|p| linux::resolve_icon_path(p.as_str()));
let icon = e
.icon
.as_ref()
.and_then(|p| linux::resolve_icon_path(p.as_str()));
#[cfg(not(target_os = "linux"))]
let icon: Option<String> = None;
let exec = e.exec.as_str().to_string();
@@ -117,13 +120,16 @@ impl AppsPlugin {
}
fn initials(name_lc: &str) -> String {
name_lc.split_whitespace().filter_map(|w| w.chars().next()).collect()
name_lc
.split_whitespace()
.filter_map(|w| w.chars().next())
.collect()
}
fn score_match(name: &str, query: &str) -> Option<u32> {
use nucleo_matcher::{
pattern::{CaseMatching, Normalization, Pattern},
Config, Matcher, Utf32Str,
pattern::{CaseMatching, Normalization, Pattern},
};
let mut matcher = Matcher::new(Config::DEFAULT);
@@ -136,7 +142,11 @@ fn score_match(name: &str, query: &str) -> Option<u32> {
if let Some(s) = score {
let name_lc = name.to_lowercase();
let query_lc = query.to_lowercase();
let bonus: u32 = if initials(&name_lc).starts_with(&query_lc) { 20 } else { 0 };
let bonus: u32 = if initials(&name_lc).starts_with(&query_lc) {
20
} else {
0
};
Some(s.saturating_add(bonus))
} else {
None
@@ -162,7 +172,9 @@ impl Plugin for AppsPlugin {
async fn search(&self, query: &str) -> Vec<SearchResult> {
if query.is_empty() {
return self.frecency.top_ids(5)
return self
.frecency
.top_ids(5)
.iter()
.filter_map(|id| {
let e = self.entries.get(id)?;
@@ -185,7 +197,10 @@ impl Plugin for AppsPlugin {
.values()
.filter_map(|e| {
let score = score_match(e.name.as_str(), query).or_else(|| {
e.keywords_lc.iter().any(|k| k.contains(&query_lc)).then_some(50)
e.keywords_lc
.iter()
.any(|k| k.contains(&query_lc))
.then_some(50)
})?;
Some(SearchResult {
id: ResultId::new(&e.id),
@@ -238,7 +253,14 @@ mod tests {
Self {
entries: entries
.into_iter()
.map(|(n, e, kw)| (n.to_string(), e.to_string(), None, kw.into_iter().map(|s| s.to_string()).collect()))
.map(|(n, e, kw)| {
(
n.to_string(),
e.to_string(),
None,
kw.into_iter().map(|s| s.to_string()).collect(),
)
})
.collect(),
}
}
@@ -261,20 +283,29 @@ mod tests {
#[tokio::test]
async fn apps_prefix_match() {
let p = AppsPlugin::new(MockSource::with(vec![("Firefox", "firefox")]), ephemeral_frecency());
let p = AppsPlugin::new(
MockSource::with(vec![("Firefox", "firefox")]),
ephemeral_frecency(),
);
let results = p.search("fire").await;
assert_eq!(results[0].title.as_str(), "Firefox");
}
#[tokio::test]
async fn apps_no_match_returns_empty() {
let p = AppsPlugin::new(MockSource::with(vec![("Firefox", "firefox")]), ephemeral_frecency());
let p = AppsPlugin::new(
MockSource::with(vec![("Firefox", "firefox")]),
ephemeral_frecency(),
);
assert!(p.search("zz").await.is_empty());
}
#[tokio::test]
async fn apps_empty_query_no_frecency_returns_empty() {
let p = AppsPlugin::new(MockSource::with(vec![("Firefox", "firefox")]), ephemeral_frecency());
let p = AppsPlugin::new(
MockSource::with(vec![("Firefox", "firefox")]),
ephemeral_frecency(),
);
assert!(p.search("").await.is_empty());
}
@@ -322,9 +353,15 @@ mod tests {
#[tokio::test]
async fn apps_fuzzy_typo_match() {
let p = AppsPlugin::new(MockSource::with(vec![("Firefox", "firefox")]), ephemeral_frecency());
let p = AppsPlugin::new(
MockSource::with(vec![("Firefox", "firefox")]),
ephemeral_frecency(),
);
let results = p.search("frefox").await;
assert!(!results.is_empty(), "nucleo should fuzzy-match 'frefox' to 'Firefox'");
assert!(
!results.is_empty(),
"nucleo should fuzzy-match 'frefox' to 'Firefox'"
);
assert!(results[0].score.value() > 0);
}

View File

@@ -1,7 +1,7 @@
use std::path::Path;
use crate::{AppName, DesktopEntry, DesktopEntrySource, ExecCommand, IconPath};
use crate::humanize_category;
use crate::{AppName, DesktopEntry, DesktopEntrySource, ExecCommand, IconPath};
pub struct FsDesktopEntrySource;
@@ -154,13 +154,15 @@ fn parse_desktop_file(path: &Path) -> Option<DesktopEntry> {
"Type" if !is_application => is_application = value.trim() == "Application",
"NoDisplay" => no_display = value.trim().eq_ignore_ascii_case("true"),
"Categories" if category.is_none() => {
category = value.trim()
category = value
.trim()
.split(';')
.find(|s| !s.is_empty())
.map(|s| humanize_category(s.trim()));
}
"Keywords" if keywords.is_empty() => {
keywords = value.trim()
keywords = value
.trim()
.split(';')
.filter(|s| !s.is_empty())
.map(|s| s.trim().to_string())
@@ -202,7 +204,10 @@ mod exec_tests {
#[test]
fn preserves_quoted_value() {
assert_eq!(clean_exec(r#"app --arg="value" %U"#), r#"app --arg="value""#);
assert_eq!(
clean_exec(r#"app --arg="value" %U"#),
r#"app --arg="value""#
);
}
#[test]

View File

@@ -22,8 +22,8 @@ fn strip_numeric_separators(expr: &str) -> String {
}
const MATH_FNS: &[&str] = &[
"sqrt", "sin", "cos", "tan", "asin", "acos", "atan",
"ln", "log2", "log10", "exp", "abs", "ceil", "floor", "round",
"sqrt", "sin", "cos", "tan", "asin", "acos", "atan", "ln", "log2", "log10", "exp", "abs",
"ceil", "floor", "round",
];
fn should_eval(query: &str) -> bool {
@@ -36,8 +36,8 @@ fn should_eval(query: &str) -> bool {
|| MATH_FNS.iter().any(|f| q.starts_with(f))
}
static MATH_CTX: LazyLock<evalexpr::HashMapContext<evalexpr::DefaultNumericTypes>> =
LazyLock::new(|| {
static MATH_CTX: LazyLock<evalexpr::HashMapContext<evalexpr::DefaultNumericTypes>> = LazyLock::new(
|| {
use evalexpr::*;
context_map! {
"pi" => float std::f64::consts::PI,
@@ -59,7 +59,8 @@ static MATH_CTX: LazyLock<evalexpr::HashMapContext<evalexpr::DefaultNumericTypes
"round" => Function::new(|a: &Value<DefaultNumericTypes>| Ok(Value::from_float(a.as_number()?.round())))
}
.expect("static math context must be valid")
});
},
);
#[async_trait]
impl Plugin for CalcPlugin {

View File

@@ -76,11 +76,7 @@ impl Plugin for FilesPlugin {
let full_path = entry.path();
let name = entry.file_name().to_string_lossy().to_string();
let is_dir = full_path.is_dir();
let title = if is_dir {
format!("{name}/")
} else {
name
};
let title = if is_dir { format!("{name}/") } else { name };
let path_str = full_path.to_string_lossy().to_string();
SearchResult {
id: ResultId::new(format!("file-{i}")),