Files
owlry/crates/owlry-rune/src/api.rs
vikingowl 384dd016a0 feat: convert to workspace with native plugin architecture
BREAKING: Restructure from monolithic binary to modular plugin ecosystem

Architecture changes:
- Convert to Cargo workspace with crates/ directory
- Create owlry-plugin-api crate with ABI-stable interface (abi_stable)
- Move core binary to crates/owlry/
- Extract providers to native plugin crates (13 plugins)
- Add owlry-lua crate for Lua plugin runtime

Plugin system:
- Plugins loaded from /usr/lib/owlry/plugins/*.so
- Widget providers refresh automatically (universal, not hardcoded)
- Per-plugin config via [plugins.<name>] sections in config.toml
- Backwards compatible with [providers] config format

New features:
- just install-local: build and install core + all plugins
- Plugin config: weather and pomodoro read from [plugins.*]
- HostAPI for plugins: notifications, logging

Documentation:
- Update README with new package structure
- Add docs/PLUGINS.md with all plugin documentation
- Add docs/PLUGIN_DEVELOPMENT.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 03:01:37 +01:00

131 lines
3.4 KiB
Rust

//! Owlry API bindings for Rune plugins
//!
//! This module provides the `owlry` module that Rune plugins can use.
use rune::{ContextError, Module};
use std::sync::Mutex;
use owlry_plugin_api::{PluginItem, RString};
/// Provider registration info
#[derive(Debug, Clone)]
pub struct ProviderRegistration {
pub name: String,
pub display_name: String,
pub type_id: String,
pub default_icon: String,
pub is_static: bool,
pub prefix: Option<String>,
}
/// An item returned by a provider
///
/// Used for converting Rune plugin items to FFI format.
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct Item {
pub id: String,
pub name: String,
pub description: Option<String>,
pub icon: Option<String>,
pub command: String,
pub terminal: bool,
pub keywords: Vec<String>,
}
impl Item {
/// Convert to PluginItem for FFI
#[allow(dead_code)]
pub fn to_plugin_item(&self) -> PluginItem {
let mut item = PluginItem::new(
RString::from(self.id.as_str()),
RString::from(self.name.as_str()),
RString::from(self.command.as_str()),
);
if let Some(ref desc) = self.description {
item = item.with_description(desc.clone());
}
if let Some(ref icon) = self.icon {
item = item.with_icon(icon.clone());
}
item.with_terminal(self.terminal)
.with_keywords(self.keywords.clone())
}
}
/// Global state for provider registrations (thread-safe)
pub static REGISTRATIONS: Mutex<Vec<ProviderRegistration>> = Mutex::new(Vec::new());
/// Create the owlry module for Rune
pub fn module() -> Result<Module, ContextError> {
let mut module = Module::with_crate("owlry")?;
// Register logging functions using builder pattern
module.function("log_info", log_info).build()?;
module.function("log_debug", log_debug).build()?;
module.function("log_warn", log_warn).build()?;
module.function("log_error", log_error).build()?;
Ok(module)
}
// ============================================================================
// Logging Functions
// ============================================================================
fn log_info(message: &str) {
log::info!("[Rune] {}", message);
}
fn log_debug(message: &str) {
log::debug!("[Rune] {}", message);
}
fn log_warn(message: &str) {
log::warn!("[Rune] {}", message);
}
fn log_error(message: &str) {
log::error!("[Rune] {}", message);
}
/// Get all provider registrations
pub fn get_registrations() -> Vec<ProviderRegistration> {
REGISTRATIONS.lock().unwrap().clone()
}
/// Clear all registrations (for testing or reloading)
pub fn clear_registrations() {
REGISTRATIONS.lock().unwrap().clear();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_item_creation() {
let item = Item {
id: "test-1".to_string(),
name: "Test Item".to_string(),
description: Some("A test".to_string()),
icon: Some("test-icon".to_string()),
command: "echo test".to_string(),
terminal: false,
keywords: vec!["test".to_string()],
};
let plugin_item = item.to_plugin_item();
assert_eq!(plugin_item.id.as_str(), "test-1");
assert_eq!(plugin_item.name.as_str(), "Test Item");
}
#[test]
fn test_module_creation() {
let module = module();
assert!(module.is_ok());
}
}