use crate::vault::application::desktop_entry_extracter;
pub use mpris;
pub use notification_manager::set_notification;
pub use rust_fuzzy_search::fuzzy_search_best_n;
use std::{
env,
ffi::OsStr,
path::{Component, Path, PathBuf},
sync::OnceLock,
};
use zbus::blocking::{Connection, Proxy};
mod application;
mod notification_manager;
pub static NOTIFICATION_EVENT: OnceLock<BlockingNotification> = OnceLock::new();
#[derive(Default)]
pub struct BlockingNotification;
impl BlockingNotification {
pub fn call_close(
&self,
id: u32,
reason: CloseReason,
) -> Result<(), Box<dyn std::error::Error>> {
let conn = Connection::session()?;
let proxy = Proxy::new(
&conn,
"org.freedesktop.Notifications",
"/org/freedesktop/Notifications",
"org.freedesktop.Notifications",
)?;
proxy.call_noreply("NotificationClosed", &(id, reason as u32))?;
Ok(())
}
}
pub trait NotificationManager {
fn new_notification(&self, notification: Notification) -> Result<(), NotiError>;
fn close_notification(&self, id: u32) -> Result<(), NotiError>;
}
#[derive(Debug)]
pub enum CloseReason {
Expired = 1,
Dismissed = 2,
ByCall = 3,
Undefined = 4,
}
#[derive(Debug)]
pub enum NotiError {
MessageUnprocessed,
MessageCloseFailed,
}
#[derive(Debug, Clone)]
pub struct Notification {
pub id: u32,
pub appname: String,
pub summary: String,
pub subtitle: Option<String>,
pub body: String,
pub icon: String,
pub hints: Vec<Hint>,
pub actions: Vec<String>,
pub timeout: Timeout,
}
#[derive(Debug, Clone)]
pub enum Hint {
ActionIcons(bool),
Category(String),
DesktopEntry(String),
ImagePath(String),
Resident(bool),
SoundFile(String),
SoundName(String),
SuppressSound(bool),
Transient(bool),
X(i32),
Y(i32),
Urgency(Urgency),
Invalid,
}
#[derive(Debug, Clone)]
pub enum Urgency {
Low = 0,
Normal = 1,
Critical = 2,
}
#[derive(Debug, Clone)]
pub enum Timeout {
Default,
Never,
Milliseconds(i32),
}
#[derive(Debug, Clone)]
pub struct AppSelector {
pub app_list: Vec<AppData>,
}
impl Default for AppSelector {
fn default() -> Self {
let data_dirs: String =
env::var("XDG_DATA_DIRS").expect("XDG_DATA_DIRS couldn't be fetched");
let mut app_line_data: Vec<AppData> = Vec::new();
let mut data_dirs_vec = data_dirs.split(':').collect::<Vec<_>>();
data_dirs_vec.push("/home/ramayen/.local/share/");
for dir in data_dirs_vec.iter() {
if Path::new(dir).is_dir() {
for inner_dir in Path::new(dir)
.read_dir()
.expect("Couldn't read the directory")
.flatten()
{
if *inner_dir
.path()
.components()
.collect::<Vec<_>>()
.last()
.unwrap()
== Component::Normal(OsStr::new("applications"))
{
let app_dir: PathBuf = inner_dir.path();
for entry_or_dir in
app_dir.read_dir().expect("Couldn't read app dir").flatten()
{
if entry_or_dir.path().is_dir() {
println!("Encountered a directory");
} else if entry_or_dir.path().extension() == Some(OsStr::new("desktop"))
{
let new_data: Vec<Option<AppData>> =
desktop_entry_extracter(entry_or_dir.path());
let filtered_data: Vec<AppData> = new_data
.iter()
.filter_map(|val| val.to_owned())
.filter(|new| {
!app_line_data.iter().any(|existing| {
existing.desktop_file_id == new.desktop_file_id
})
})
.collect();
app_line_data.extend(filtered_data);
} else if entry_or_dir.path().is_symlink() {
println!("GOt the symlink");
} else {
}
}
}
}
}
}
AppSelector {
app_list: app_line_data,
}
}
}
impl AppSelector {
pub fn get_primary(&self) -> impl Iterator<Item = &AppData> {
self.app_list.iter().filter(|val| val.is_primary)
}
pub fn get_all(&self) -> impl Iterator<Item = &AppData> {
self.app_list.iter()
}
pub fn query_primary(&self, query_val: &str, size: usize) -> Vec<&AppData> {
let query_val = query_val.to_lowercase();
let query_list = self
.app_list
.iter()
.filter(|val| val.is_primary)
.map(|val| val.name.to_lowercase())
.collect::<Vec<String>>();
let query_list: Vec<&str> = query_list.iter().map(|v| v.as_str()).collect();
let best_match_names: Vec<&str> =
fuzzy_search_best_n(query_val.as_str(), &query_list, size)
.iter()
.map(|val| val.0)
.collect();
best_match_names
.iter()
.map(|app_name| {
self.app_list
.iter()
.find(|val| val.name.to_lowercase().as_str() == *app_name)
.unwrap()
})
.collect::<Vec<&AppData>>()
}
pub fn query_all(&self, query_val: &str, size: usize) -> Vec<&AppData> {
let query_val = query_val.to_lowercase();
let query_list = self
.app_list
.iter()
.map(|val| val.name.to_lowercase())
.collect::<Vec<String>>();
let query_list: Vec<&str> = query_list.iter().map(|v| v.as_ref()).collect();
let best_match_names: Vec<&str> =
fuzzy_search_best_n(query_val.as_str(), &query_list, size)
.iter()
.map(|val| val.0)
.collect();
best_match_names
.iter()
.map(|app_name| {
self.app_list
.iter()
.find(|val| val.name.to_lowercase().as_str() == *app_name)
.unwrap()
})
.collect::<Vec<&AppData>>()
}
}
#[derive(Debug, Clone)]
pub struct AppData {
pub desktop_file_id: String,
pub is_primary: bool,
pub image_path: Option<String>,
pub name: String,
pub exec_comm: Option<String>,
}