refactor: centralize path handling with XDG Base Directory compliance
- Add src/paths.rs module for all XDG path lookups - Move scripts from ~/.config to ~/.local/share (XDG data) - Use $XDG_CONFIG_HOME for browser bookmark paths - Add dev-logging feature flag for verbose debug output - Add dev-install profile for testable release builds - Remove CLAUDE.md from version control BREAKING: Scripts directory moved from ~/.config/owlry/scripts/ to ~/.local/share/owlry/scripts/ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use super::{LaunchItem, Provider, ProviderType};
|
||||
use crate::paths;
|
||||
use freedesktop_desktop_entry::{DesktopEntry, Iter};
|
||||
use log::{debug, warn};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Clean desktop file field codes from command string.
|
||||
/// Removes %f, %F, %u, %U, %d, %D, %n, %N, %i, %c, %k, %v, %m field codes
|
||||
@@ -75,25 +75,8 @@ impl ApplicationProvider {
|
||||
Self { items: Vec::new() }
|
||||
}
|
||||
|
||||
fn get_application_dirs() -> Vec<PathBuf> {
|
||||
let mut dirs = Vec::new();
|
||||
|
||||
// User applications
|
||||
if let Some(data_home) = dirs::data_dir() {
|
||||
dirs.push(data_home.join("applications"));
|
||||
}
|
||||
|
||||
// System applications
|
||||
dirs.push(PathBuf::from("/usr/share/applications"));
|
||||
dirs.push(PathBuf::from("/usr/local/share/applications"));
|
||||
|
||||
// Flatpak applications
|
||||
if let Some(data_home) = dirs::data_dir() {
|
||||
dirs.push(data_home.join("flatpak/exports/share/applications"));
|
||||
}
|
||||
dirs.push(PathBuf::from("/var/lib/flatpak/exports/share/applications"));
|
||||
|
||||
dirs
|
||||
fn get_application_dirs() -> Vec<std::path::PathBuf> {
|
||||
paths::system_data_dirs()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::paths;
|
||||
use crate::providers::{LaunchItem, Provider, ProviderType};
|
||||
use log::{debug, warn};
|
||||
use serde::Deserialize;
|
||||
@@ -27,8 +28,8 @@ impl BookmarksProvider {
|
||||
fn load_firefox_bookmarks(&mut self) {
|
||||
// Firefox stores bookmarks in places.sqlite
|
||||
// The file is locked when Firefox is running, so we read from backup
|
||||
let firefox_dir = match dirs::home_dir() {
|
||||
Some(h) => h.join(".mozilla").join("firefox"),
|
||||
let firefox_dir = match paths::firefox_dir() {
|
||||
Some(d) => d,
|
||||
None => return,
|
||||
};
|
||||
|
||||
@@ -99,29 +100,10 @@ impl BookmarksProvider {
|
||||
}
|
||||
|
||||
fn load_chrome_bookmarks(&mut self) {
|
||||
// Chrome/Chromium bookmarks are in JSON format
|
||||
let home = match dirs::home_dir() {
|
||||
Some(h) => h,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Try multiple browser paths
|
||||
let bookmark_paths = [
|
||||
// Chrome
|
||||
home.join(".config/google-chrome/Default/Bookmarks"),
|
||||
// Chromium
|
||||
home.join(".config/chromium/Default/Bookmarks"),
|
||||
// Brave
|
||||
home.join(".config/BraveSoftware/Brave-Browser/Default/Bookmarks"),
|
||||
// Edge
|
||||
home.join(".config/microsoft-edge/Default/Bookmarks"),
|
||||
// Vivaldi
|
||||
home.join(".config/vivaldi/Default/Bookmarks"),
|
||||
];
|
||||
|
||||
for path in &bookmark_paths {
|
||||
// Chrome/Chromium bookmarks are in JSON format (XDG config paths)
|
||||
for path in paths::chromium_bookmark_paths() {
|
||||
if path.exists() {
|
||||
self.read_chrome_bookmarks(path);
|
||||
self.read_chrome_bookmarks(&path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::paths;
|
||||
use crate::providers::{LaunchItem, ProviderType};
|
||||
use log::{debug, warn};
|
||||
use std::process::Command;
|
||||
@@ -106,7 +107,7 @@ impl FileSearchProvider {
|
||||
|
||||
fn search_with_fd(&self, pattern: &str) -> Vec<LaunchItem> {
|
||||
// fd searches from home directory by default
|
||||
let home = dirs::home_dir().unwrap_or_default();
|
||||
let home = paths::home().unwrap_or_default();
|
||||
|
||||
let output = match Command::new("fd")
|
||||
.args([
|
||||
@@ -132,7 +133,7 @@ impl FileSearchProvider {
|
||||
}
|
||||
|
||||
fn search_with_locate(&self, pattern: &str) -> Vec<LaunchItem> {
|
||||
let home = dirs::home_dir().unwrap_or_default();
|
||||
let home = paths::home().unwrap_or_default();
|
||||
|
||||
let output = match Command::new("locate")
|
||||
.args([
|
||||
|
||||
@@ -30,6 +30,9 @@ use fuzzy_matcher::FuzzyMatcher;
|
||||
use fuzzy_matcher::skim::SkimMatcherV2;
|
||||
use log::info;
|
||||
|
||||
#[cfg(feature = "dev-logging")]
|
||||
use log::debug;
|
||||
|
||||
use crate::data::FrecencyStore;
|
||||
|
||||
/// Represents a single searchable/launchable item
|
||||
@@ -288,12 +291,16 @@ impl ProviderManager {
|
||||
frecency: &FrecencyStore,
|
||||
frecency_weight: f64,
|
||||
) -> Vec<(LaunchItem, i64)> {
|
||||
#[cfg(feature = "dev-logging")]
|
||||
debug!("[Search] query={:?}, max={}, frecency_weight={}", query, max_results, frecency_weight);
|
||||
|
||||
let mut results: Vec<(LaunchItem, i64)> = Vec::new();
|
||||
|
||||
// Check for calculator query (= or calc prefix)
|
||||
if CalculatorProvider::is_calculator_query(query) {
|
||||
if let Some(calc_result) = self.calculator.evaluate(query) {
|
||||
// Calculator results get a high score to appear first
|
||||
#[cfg(feature = "dev-logging")]
|
||||
debug!("[Search] Calculator result: {}", calc_result.name);
|
||||
results.push((calc_result, 10000));
|
||||
}
|
||||
}
|
||||
@@ -323,6 +330,8 @@ impl ProviderManager {
|
||||
// Check for file search query
|
||||
if FileSearchProvider::is_file_query(query) {
|
||||
let file_results = self.filesearch.evaluate(query);
|
||||
#[cfg(feature = "dev-logging")]
|
||||
debug!("[Search] File search returned {} results", file_results.len());
|
||||
for (idx, item) in file_results.into_iter().enumerate() {
|
||||
// Score decreases for each result to maintain order
|
||||
results.push((item, 8000 - idx as i64));
|
||||
@@ -387,6 +396,18 @@ impl ProviderManager {
|
||||
results.extend(search_results);
|
||||
results.sort_by(|a, b| b.1.cmp(&a.1));
|
||||
results.truncate(max_results);
|
||||
|
||||
#[cfg(feature = "dev-logging")]
|
||||
{
|
||||
debug!("[Search] Returning {} results", results.len());
|
||||
for (i, (item, score)) in results.iter().take(5).enumerate() {
|
||||
debug!("[Search] #{}: {} (score={}, provider={:?})", i + 1, item.name, score, item.provider);
|
||||
}
|
||||
if results.len() > 5 {
|
||||
debug!("[Search] ... and {} more", results.len() - 5);
|
||||
}
|
||||
}
|
||||
|
||||
results
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use crate::paths;
|
||||
use crate::providers::{LaunchItem, Provider, ProviderType};
|
||||
use log::{debug, warn};
|
||||
use std::fs;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Custom scripts provider - runs user scripts from ~/.config/owlry/scripts/
|
||||
/// Custom scripts provider - runs user scripts from `$XDG_DATA_HOME/owlry/scripts/`
|
||||
pub struct ScriptsProvider {
|
||||
items: Vec<LaunchItem>,
|
||||
}
|
||||
@@ -14,14 +15,10 @@ impl ScriptsProvider {
|
||||
Self { items: Vec::new() }
|
||||
}
|
||||
|
||||
fn scripts_dir() -> Option<PathBuf> {
|
||||
dirs::config_dir().map(|p| p.join("owlry").join("scripts"))
|
||||
}
|
||||
|
||||
fn load_scripts(&mut self) {
|
||||
self.items.clear();
|
||||
|
||||
let scripts_dir = match Self::scripts_dir() {
|
||||
let scripts_dir = match paths::scripts_dir() {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
debug!("Could not determine scripts directory");
|
||||
@@ -32,7 +29,7 @@ impl ScriptsProvider {
|
||||
if !scripts_dir.exists() {
|
||||
debug!("Scripts directory not found at {:?}", scripts_dir);
|
||||
// Create the directory for the user
|
||||
if let Err(e) = fs::create_dir_all(&scripts_dir) {
|
||||
if let Err(e) = paths::ensure_dir(&scripts_dir) {
|
||||
warn!("Failed to create scripts directory: {}", e);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::paths;
|
||||
use crate::providers::{LaunchItem, Provider, ProviderType};
|
||||
use log::{debug, warn};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// SSH connections provider - parses ~/.ssh/config
|
||||
pub struct SshProvider {
|
||||
@@ -27,8 +27,8 @@ impl SshProvider {
|
||||
self.terminal_command = terminal.to_string();
|
||||
}
|
||||
|
||||
fn ssh_config_path() -> Option<PathBuf> {
|
||||
dirs::home_dir().map(|p| p.join(".ssh").join("config"))
|
||||
fn ssh_config_path() -> Option<std::path::PathBuf> {
|
||||
paths::ssh_config()
|
||||
}
|
||||
|
||||
fn parse_ssh_config(&mut self) {
|
||||
|
||||
Reference in New Issue
Block a user