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
use waycast_core::{LauncherPlugin, LauncherListItem};
use waycast_macros::plugin;
pub struct MyPlugin {
// Your custom fields
data: Vec<String>,
}
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<Box<dyn LauncherListItem>> {
// Return default items
Vec::new()
}
fn my_filter(plugin: &MyPlugin, query: &str) -> Vec<Box<dyn LauncherListItem>> {
// Filter and return matching items
Vec::new()
}
Parameters
name
: Required - String literal for plugin namepriority
: Optional - Integer priority (default: 100)description
: Optional - String descriptionprefix
: Optional - String prefix for queriesby_prefix_only
: Optional - Boolean, whether plugin only responds to prefix queries (default: false)init
: Optional - Function name for initializationdefault_list
: Optional - Function name that returns default itemsfilter
: Optional - Function name that filters items based on query
Function Signatures
// All functions are optional
fn init_function(plugin: &YourPluginType) {
// Initialize plugin
}
fn default_list_function(plugin: &YourPluginType) -> Vec<Box<dyn LauncherListItem>> {
// Return default items when no query
}
fn filter_function(plugin: &YourPluginType, query: &str) -> Vec<Box<dyn LauncherListItem>> {
// Return filtered items based on query
}
launcher_entry!
Generates LauncherListItem
trait method implementations inside an impl LauncherListItem
block.
Usage
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 returningString
unique identifiertitle
: Required - Expression returningString
display titledescription
: Optional - Expression returningOption<String>
descriptionicon
: Required - Expression returningString
icon name or pathexecute
: Required - Expression returningResult<(), LaunchError>
execution logic
Expression Types
All parameters accept Rust expressions:
Simple expressions:
id: self.name.clone(),
title: "My Title".to_string(),
Complex expressions with blocks:
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
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
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<PathBuf>,
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<Box<dyn LauncherListItem>> {
// 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 definitionssyn
,quote
, andproc-macro2
dependencies (handled automatically)