147 lines
4.3 KiB
Rust

use std::collections::HashMap;
use std::sync::Arc;
pub mod cache;
#[derive(Debug)]
pub enum LaunchError {
CouldNotLaunch(String),
}
pub trait LauncherListItem {
fn id(&self) -> String;
fn title(&self) -> String;
fn description(&self) -> Option<String>;
fn execute(&self) -> Result<(), LaunchError>;
fn icon(&self) -> String;
}
pub trait LauncherPlugin {
fn init(&self) {
// Default empty init - plugins can override this
}
fn name(&self) -> String;
fn priority(&self) -> i32;
fn description(&self) -> Option<String>;
// Prefix to isolate results to only use this plugin
fn prefix(&self) -> Option<String>;
// Only search/use this plugin if the prefix was typed
fn by_prefix_only(&self) -> bool;
// Actual item searching functions
fn default_list(&self) -> Vec<Box<dyn LauncherListItem>> {
// Default empty list - plugins can override this
Vec::new()
}
fn filter(&self, query: &str) -> Vec<Box<dyn LauncherListItem>>;
}
pub struct WaycastLauncher {
plugins: Vec<Arc<dyn LauncherPlugin>>,
plugins_show_always: Vec<Arc<dyn LauncherPlugin>>,
plugins_by_prefix: HashMap<String, Arc<dyn LauncherPlugin>>,
current_results: Vec<Box<dyn LauncherListItem>>,
current_results_by_id: HashMap<String, usize>,
}
impl WaycastLauncher {
pub fn new() -> Self {
WaycastLauncher {
plugins: Vec::new(),
plugins_show_always: Vec::new(),
plugins_by_prefix: HashMap::new(),
current_results: Vec::new(),
current_results_by_id: HashMap::new(),
}
}
pub fn add_plugin(mut self, plugin: Box<dyn LauncherPlugin>) -> Self {
let p: Arc<dyn LauncherPlugin> = plugin.into();
if !p.by_prefix_only() {
self.plugins_show_always.push(Arc::clone(&p));
}
if let Some(prefix) = p.prefix() {
self.plugins_by_prefix.insert(prefix, Arc::clone(&p));
}
self.plugins.push(p);
self
}
pub fn init(mut self) -> Self {
for p in &self.plugins {
p.init();
}
self.plugins.sort_by(|a, b| b.priority().cmp(&a.priority()));
self.plugins_show_always
.sort_by(|a, b| b.priority().cmp(&a.priority()));
self
}
fn add_current_item(&mut self, item: Box<dyn LauncherListItem>) {
let id = item.id();
let index = self.current_results.len();
self.current_results.push(item);
self.current_results_by_id.insert(id, index);
}
fn clear_current_results(&mut self) {
self.current_results.clear();
self.current_results_by_id.clear();
}
pub fn get_default_results(&mut self) -> &Vec<Box<dyn LauncherListItem>> {
self.clear_current_results();
let mut all_entries = Vec::new();
for plugin in &self.plugins_show_always {
all_entries.extend(plugin.default_list());
}
for entry in all_entries {
self.add_current_item(entry);
}
&self.current_results
}
pub fn search(&mut self, query: &str) -> &Vec<Box<dyn LauncherListItem>> {
self.clear_current_results();
let mut all_entries = Vec::new();
for plugin in &self.plugins {
all_entries.extend(plugin.filter(query));
}
for entry in all_entries {
self.add_current_item(entry);
}
&self.current_results
}
pub fn execute_item(&self, index: usize) -> Result<(), LaunchError> {
if let Some(item) = self.current_results.get(index) {
item.execute()
} else {
Err(LaunchError::CouldNotLaunch("Invalid index".into()))
}
}
pub fn execute_item_by_id(&self, id: &str) -> Result<(), LaunchError> {
if let Some(&index) = self.current_results_by_id.get(id) {
if let Some(item) = self.current_results.get(index) {
item.execute()
} else {
Err(LaunchError::CouldNotLaunch(
"Item index out of bounds".into(),
))
}
} else {
Err(LaunchError::CouldNotLaunch("Item not found".into()))
}
}
pub fn current_results(&self) -> &Vec<Box<dyn LauncherListItem>> {
&self.current_results
}
}