diff --git a/crates/owlry-core/src/plugins/runtime_loader.rs b/crates/owlry-core/src/plugins/runtime_loader.rs index 82480ee..606d443 100644 --- a/crates/owlry-core/src/plugins/runtime_loader.rs +++ b/crates/owlry-core/src/plugins/runtime_loader.rs @@ -10,8 +10,6 @@ //! Note: This module is infrastructure for the runtime architecture. Full integration //! is pending Phase 5 (AUR Packaging) when runtime packages will be available. -#![allow(dead_code)] - use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -171,6 +169,14 @@ impl Drop for LoadedRuntime { } } +// LoadedRuntime needs to be Send + Sync because ProviderManager is shared across +// threads via Arc>. +// Safety: RuntimeHandle is an opaque FFI handle accessed only through extern "C" +// vtable functions. The same safety argument that applies to RuntimeProvider applies +// here — all access is mediated by the vtable, and the runtime itself serializes access. +unsafe impl Send for LoadedRuntime {} +unsafe impl Sync for LoadedRuntime {} + /// A provider backed by a dynamically loaded runtime pub struct RuntimeProvider { /// Runtime name (for logging) diff --git a/crates/owlry-core/src/providers/mod.rs b/crates/owlry-core/src/providers/mod.rs index 8591377..75424b4 100644 --- a/crates/owlry-core/src/providers/mod.rs +++ b/crates/owlry-core/src/providers/mod.rs @@ -25,6 +25,7 @@ use log::debug; use crate::config::Config; use crate::data::FrecencyStore; +use crate::plugins::runtime_loader::LoadedRuntime; /// Metadata descriptor for an available provider (used by IPC/daemon API) #[derive(Debug, Clone)] @@ -116,6 +117,10 @@ pub struct ProviderManager { widget_providers: Vec, /// Fuzzy matcher for search matcher: SkimMatcherV2, + /// Loaded script runtimes (Lua, Rune) — must stay alive to keep Library handles + runtimes: Vec, + /// Type IDs of providers from script runtimes (for hot-reload removal) + runtime_type_ids: std::collections::HashSet, } impl ProviderManager { @@ -134,6 +139,8 @@ impl ProviderManager { dynamic_providers: Vec::new(), widget_providers: Vec::new(), matcher: SkimMatcherV2::default(), + runtimes: Vec::new(), + runtime_type_ids: std::collections::HashSet::new(), }; // Categorize native plugins based on their declared ProviderKind and ProviderPosition @@ -180,7 +187,7 @@ impl ProviderManager { use std::sync::Arc; // Create core providers - let core_providers: Vec> = vec![ + let mut core_providers: Vec> = vec![ Box::new(ApplicationProvider::new()), Box::new(CommandProvider::new()), ]; @@ -220,7 +227,59 @@ impl ProviderManager { } }; - Self::new(core_providers, native_providers) + // Load script runtimes (Lua, Rune) for user plugins + let mut runtime_providers: Vec> = Vec::new(); + let mut runtimes: Vec = Vec::new(); + let mut runtime_type_ids = std::collections::HashSet::new(); + let owlry_version = env!("CARGO_PKG_VERSION"); + + let skip_runtimes = std::env::var("OWLRY_SKIP_RUNTIMES").is_ok(); + if !skip_runtimes { + if let Some(plugins_dir) = crate::paths::plugins_dir() { + // Try Lua runtime + match LoadedRuntime::load_lua(&plugins_dir, owlry_version) { + Ok(rt) => { + info!("Loaded Lua runtime with {} provider(s)", rt.providers().len()); + for provider in rt.create_providers() { + let type_id = format!("{}", provider.provider_type()); + runtime_type_ids.insert(type_id); + runtime_providers.push(provider); + } + runtimes.push(rt); + } + Err(e) => { + info!("Lua runtime not available: {}", e); + } + } + + // Try Rune runtime + match LoadedRuntime::load_rune(&plugins_dir, owlry_version) { + Ok(rt) => { + info!("Loaded Rune runtime with {} provider(s)", rt.providers().len()); + for provider in rt.create_providers() { + let type_id = format!("{}", provider.provider_type()); + runtime_type_ids.insert(type_id); + runtime_providers.push(provider); + } + runtimes.push(rt); + } + Err(e) => { + info!("Rune runtime not available: {}", e); + } + } + } + } // skip_runtimes + + // Merge runtime providers into core providers + for provider in runtime_providers { + info!("Registered runtime provider: {}", provider.name()); + core_providers.push(provider); + } + + let mut manager = Self::new(core_providers, native_providers); + manager.runtimes = runtimes; + manager.runtime_type_ids = runtime_type_ids; + manager } #[allow(dead_code)]