# Waycast Macros Procedural macros for the Waycast launcher framework to reduce boilerplate when implementing plugins and launcher items. ## Macros ### `plugin!` Generates `LauncherPlugin` trait method implementations inside an `impl LauncherPlugin` block. #### Usage ```rust use waycast_core::{LauncherPlugin, LauncherListItem}; use waycast_macros::plugin; pub struct MyPlugin { // Your custom fields data: Vec, } impl MyPlugin { pub fn new() -> Self { Self { data: Vec::new() } } // Your custom methods pub fn add_item(&mut self, item: String) { self.data.push(item); } } impl LauncherPlugin for MyPlugin { plugin! { name: "My Plugin", priority: 500, description: "A sample plugin", prefix: "my", by_prefix_only: false, init: my_init, default_list: my_default_list, filter: my_filter } } // Implement your plugin functions fn my_init(plugin: &MyPlugin) { println!("Initializing plugin with {} items", plugin.data.len()); } fn my_default_list(plugin: &MyPlugin) -> Vec> { // Return default items Vec::new() } fn my_filter(plugin: &MyPlugin, query: &str) -> Vec> { // Filter and return matching items Vec::new() } ``` #### Parameters - `name`: **Required** - String literal for plugin name - `priority`: Optional - Integer priority (default: 100) - `description`: Optional - String description - `prefix`: Optional - String prefix for queries - `by_prefix_only`: Optional - Boolean, whether plugin only responds to prefix queries (default: false) - `init`: Optional - Function name for initialization - `default_list`: Optional - Function name that returns default items - `filter`: Optional - Function name that filters items based on query #### Function Signatures ```rust // All functions are optional fn init_function(plugin: &YourPluginType) { // Initialize plugin } fn default_list_function(plugin: &YourPluginType) -> Vec> { // Return default items when no query } fn filter_function(plugin: &YourPluginType, query: &str) -> Vec> { // Return filtered items based on query } ``` ### `launcher_entry!` Generates `LauncherListItem` trait method implementations inside an `impl LauncherListItem` block. #### Usage ```rust use waycast_core::{LaunchError, LauncherListItem}; use waycast_macros::launcher_entry; #[derive(Clone)] pub struct MyItem { name: String, path: PathBuf, } impl LauncherListItem for MyItem { launcher_entry! { id: format!("item_{}", self.name), title: self.name.clone(), description: Some(format!("Item at {}", self.path.display())), icon: "application-x-executable".to_string(), execute: { println!("Executing {}", self.name); std::process::Command::new("xdg-open") .arg(&self.path) .spawn() .map_err(|e| LaunchError::CouldNotLaunch(e.to_string()))?; Ok(()) } } } ``` #### Parameters - `id`: **Required** - Expression returning `String` unique identifier - `title`: **Required** - Expression returning `String` display title - `description`: Optional - Expression returning `Option` description - `icon`: **Required** - Expression returning `String` icon name or path - `execute`: **Required** - Expression returning `Result<(), LaunchError>` execution logic #### Expression Types All parameters accept Rust expressions: **Simple expressions:** ```rust id: self.name.clone(), title: "My Title".to_string(), ``` **Complex expressions with blocks:** ```rust icon: { if self.is_directory() { "folder".to_string() } else { "file".to_string() } }, execute: { println!("Opening {}", self.path.display()); // Complex execution logic match std::process::Command::new("xdg-open").arg(&self.path).spawn() { Ok(_) => Ok(()), Err(e) => Err(LaunchError::CouldNotLaunch(e.to_string())), } } ``` ## Examples ### Simple Plugin Example ```rust use waycast_macros::{plugin, launcher_entry}; use waycast_core::{LaunchError, LauncherListItem, LauncherPlugin}; // Simple plugin with no state pub struct CalculatorPlugin; impl CalculatorPlugin { pub fn new() -> Self { CalculatorPlugin } } impl LauncherPlugin for CalculatorPlugin { plugin! { name: "Calculator", priority: 800, description: "Perform calculations", prefix: "calc" } } // Simple item #[derive(Clone)] struct CalcResult { expression: String, result: f64, } impl LauncherListItem for CalcResult { launcher_entry! { id: self.expression.clone(), title: format!("{} = {}", self.expression, self.result), description: Some("Calculation result".to_string()), icon: "accessories-calculator".to_string(), execute: { println!("Result: {}", self.result); Ok(()) } } } ``` ### Complex Plugin Example ```rust use waycast_macros::{plugin, launcher_entry}; use waycast_core::{LaunchError, LauncherListItem, LauncherPlugin}; use std::path::PathBuf; // Complex plugin with state and custom methods pub struct FileSearchPlugin { search_paths: Vec, max_results: usize, } impl FileSearchPlugin { pub fn new() -> Self { Self { search_paths: vec![PathBuf::from("/home")], max_results: 50, } } pub fn add_search_path(&mut self, path: PathBuf) { self.search_paths.push(path); } pub fn set_max_results(&mut self, max: usize) { self.max_results = max; } } impl LauncherPlugin for FileSearchPlugin { plugin! { name: "File Search", priority: 600, description: "Search and open files", prefix: "file", init: file_search_init, filter: file_search_filter } } fn file_search_init(plugin: &FileSearchPlugin) { println!("Initialized file search with {} paths", plugin.search_paths.len()); } fn file_search_filter(plugin: &FileSearchPlugin, query: &str) -> Vec> { // Implementation would search files and return FileEntry items Vec::new() } // Complex item with file operations #[derive(Clone)] struct FileEntry { path: PathBuf, } impl LauncherListItem for FileEntry { launcher_entry! { id: self.path.to_string_lossy().to_string(), title: self.path.file_name().unwrap().to_string_lossy().to_string(), description: Some(format!("File: {}", self.path.display())), icon: { // Complex icon detection logic let extension = self.path.extension() .and_then(|s| s.to_str()) .unwrap_or(""); match extension { "txt" | "md" => "text-x-generic", "png" | "jpg" | "jpeg" => "image-x-generic", "mp3" | "wav" | "flac" => "audio-x-generic", _ => "application-x-generic" }.to_string() }, execute: { println!("Opening file: {}", self.path.display()); std::process::Command::new("xdg-open") .arg(&self.path) .spawn() .map_err(|e| LaunchError::CouldNotLaunch(format!("Failed to open file: {}", e)))?; Ok(()) } } } ``` ## IDE Support Both macros include built-in rust-analyzer support to prevent "trait not fully implemented" errors in your editor. The macros automatically generate stub implementations that are only visible to rust-analyzer, ensuring a smooth development experience. ## Requirements - Rust 2021 edition or later - `waycast-core` crate for trait definitions - `syn`, `quote`, and `proc-macro2` dependencies (handled automatically)