load_freedesktop/
lib.rs

1pub mod cache;
2pub mod fitername;
3use std::{
4    path::PathBuf,
5    sync::{Arc, Mutex},
6};
7
8use cache::{cache_entries, get_cached_entries};
9
10use freedesktop_file_parser::EntryType;
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub enum IconPath {
15    None,
16    Image(String),
17    Svg(String),
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct EntryData {
22    pub name: String,
23    pub score : Option<i32>,
24    // pub filter_name : fitername::FilterName,
25    pub exec: String,
26    icon: IconPath,
27}
28impl EntryData {
29    fn new(data: freedesktop_file_parser::DesktopEntry) -> Option<Self> {
30        if let EntryType::Application(app_data) = data.entry_type {
31            let name = data.name.default;
32            let exec = app_data.exec?;
33            let icon = match data.icon {
34                None => IconPath::None,
35                Some(icon) => {
36                    let icon = icon.get_icon_path();
37                    if let Some(path) = icon {
38                        let path = path.to_string_lossy().to_string();
39                        if path.ends_with("svg") {
40                            IconPath::Svg(path)
41                        } else {
42                            IconPath::Image(path)
43                        }
44                    } else {
45                        IconPath::None
46                    }
47                }
48            };
49            // let filter_name = fitername::FilterName::new(&name);
50            Some(EntryData { name, score : None, exec, icon })
51        } else {
52            None
53        }
54    }
55    pub fn get_icon_path(&self) -> IconPath {
56        self.icon.clone()
57    }
58
59    pub fn run(&self) {
60        use std::process::Command;
61        let mut command = Command::new("sh");
62        let exenc = self
63            .exec
64            .clone()
65            .split_whitespace()
66            .filter(|s| *s != "%u" && *s != "%U")
67            .fold("".to_string(), |a, s| a + s + " ");
68        command.args(["-c", &exenc]);
69        let _ = command.spawn();
70    }
71}
72
73fn get_paths() -> Vec<PathBuf> {
74    let homepath = std::env::home_dir()
75        .unwrap_or("~".into())
76        .to_string_lossy()
77        .to_string();
78    let p = homepath + "/.local/share/applications";
79    let paths = [
80        "/usr/share/applications",
81        &p,
82        // "~/.local/share/applications",
83        "/var/lib/flatpak/exports/share/applications/",
84    ];
85    paths
86        .into_iter()
87        .filter_map(|path| std::fs::read_dir(path).map(Some).unwrap_or(None))
88        .flat_map(|dir_p| {
89            dir_p
90                .filter_map(|m_dir_e| m_dir_e.ok().map(|p| p.path()))
91                .collect::<Vec<_>>()
92        })
93        .filter(|path| path.extension().is_some_and(|s| s == "desktop"))
94        .collect::<Vec<_>>()
95}
96
97fn load_entry_single(entry: Arc<Mutex<Option<EntryData>>>, path: PathBuf) -> Option<()> {
98    use freedesktop_file_parser::parse;
99    let path = std::fs::read_to_string(path).ok()?;
100    let entry_data = parse(&path).ok()?;
101    if let Ok(mut entry) = entry.lock() {
102        let entryy = EntryData::new(entry_data.entry)?;
103        *entry = Some(entryy);
104    }
105    Some(())
106}
107
108fn load_entries_no_cache() -> Vec<EntryData> {
109    use freedesktop_file_parser::parse;
110    let paths = get_paths();
111    let entries = paths
112        .into_iter()
113        .filter_map(|path| std::fs::read_to_string(path).ok())
114        .filter_map(|s| parse(&s).ok())
115        .collect::<Vec<_>>();
116    let mut name_set = std::collections::hash_set::HashSet::new();
117    let entries = entries
118        .into_iter()
119        .filter_map(|entry| EntryData::new(entry.entry))
120        .filter(|entry|{
121            if name_set.contains(&entry.name){
122                false
123            }else{
124                name_set.insert(entry.name.clone());
125                true
126            }
127        })
128        .collect::<Vec<_>>();
129    let _ = cache_entries(entries.clone());
130    entries
131}
132
133pub fn load_entries() -> Vec<EntryData> {
134    match get_cached_entries() {
135        Ok(ent) => {
136            std::thread::spawn(|| {
137                let _ = load_entries_no_cache();
138            });
139            ent
140        }
141        Err(_) => load_entries_no_cache(),
142    }
143}