diff --git a/crates/owlry-lua/src/lib.rs b/crates/owlry-lua/src/lib.rs index 65880d5..06d0c1b 100644 --- a/crates/owlry-lua/src/lib.rs +++ b/crates/owlry-lua/src/lib.rs @@ -27,7 +27,7 @@ mod manifest; mod runtime; use abi_stable::std_types::{ROption, RStr, RString, RVec}; -use owlry_plugin_api::{PluginItem, ProviderKind}; +use owlry_plugin_api::PluginItem; use std::collections::HashMap; use std::path::PathBuf; @@ -94,24 +94,22 @@ impl RuntimeHandle { } /// Provider info from a Lua plugin +/// +/// Must match ScriptProviderInfo layout in owlry-core/src/plugins/runtime_loader.rs #[repr(C)] pub struct LuaProviderInfo { - /// Full provider ID: "plugin_id:provider_name" - pub id: RString, - /// Plugin ID this provider belongs to - pub plugin_id: RString, - /// Provider name within the plugin - pub provider_name: RString, + /// Provider name (used as vtable refresh/query key: "plugin_id:provider_name") + pub name: RString, /// Display name pub display_name: RString, - /// Optional prefix trigger - pub prefix: ROption, - /// Icon name - pub icon: RString, - /// Provider type (static/dynamic) - pub provider_type: ProviderKind, /// Type ID for filtering pub type_id: RString, + /// Icon name + pub default_icon: RString, + /// Whether this is a static provider (true) or dynamic (false) + pub is_static: bool, + /// Optional prefix trigger + pub prefix: ROption, } /// Internal runtime state @@ -175,21 +173,14 @@ impl LuaRuntimeState { if let Ok(registrations) = plugin.get_provider_registrations() { for reg in registrations { let full_id = format!("{}:{}", plugin_id, reg.name); - let provider_type = if reg.is_dynamic { - ProviderKind::Dynamic - } else { - ProviderKind::Static - }; providers.push(LuaProviderInfo { - id: RString::from(full_id), - plugin_id: RString::from(plugin_id.as_str()), - provider_name: RString::from(reg.name.as_str()), + name: RString::from(full_id), display_name: RString::from(reg.display_name.as_str()), - prefix: reg.prefix.map(RString::from).into(), - icon: RString::from(reg.default_icon.as_str()), - provider_type, type_id: RString::from(reg.type_id.as_str()), + default_icon: RString::from(reg.default_icon.as_str()), + is_static: !reg.is_dynamic, + prefix: reg.prefix.map(RString::from).into(), }); } } diff --git a/crates/owlry-rune/src/api.rs b/crates/owlry-rune/src/api.rs index 5f28498..727b786 100644 --- a/crates/owlry-rune/src/api.rs +++ b/crates/owlry-rune/src/api.rs @@ -2,7 +2,7 @@ //! //! This module provides the `owlry` module that Rune plugins can use. -use rune::{ContextError, Module}; +use rune::{Any, ContextError, Module}; use std::sync::Mutex; use owlry_plugin_api::{PluginItem, RString}; @@ -20,9 +20,8 @@ pub struct ProviderRegistration { /// An item returned by a provider /// -/// Used for converting Rune plugin items to FFI format. -#[derive(Debug, Clone)] -#[allow(dead_code)] +/// Exposed to Rune scripts as `owlry::Item`. +#[derive(Debug, Clone, Any)] pub struct Item { pub id: String, pub name: String, @@ -34,8 +33,38 @@ pub struct Item { } impl Item { + /// Create a new item (exposed to Rune as Item::new) + fn rune_new(id: &str, name: &str, command: &str) -> Self { + Self { + id: id.to_string(), + name: name.to_string(), + command: command.to_string(), + description: None, + icon: None, + terminal: false, + keywords: Vec::new(), + } + } + + /// Set description (builder pattern for Rune) + fn rune_description(mut self, desc: &str) -> Self { + self.description = Some(desc.to_string()); + self + } + + /// Set icon (builder pattern for Rune) + fn rune_icon(mut self, icon: &str) -> Self { + self.icon = Some(icon.to_string()); + self + } + + /// Set keywords (builder pattern for Rune) + fn rune_keywords(mut self, keywords: Vec) -> Self { + self.keywords = keywords; + self + } + /// 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()), @@ -62,7 +91,19 @@ pub static REGISTRATIONS: Mutex> = Mutex::new(Vec::new pub fn module() -> Result { let mut module = Module::with_crate("owlry")?; - // Register logging functions using builder pattern + // Register Item type with constructor and builder methods + module.ty::()?; + module + .function("Item::new", Item::rune_new) + .build()?; + module + .associated_function("description", Item::rune_description)?; + module + .associated_function("icon", Item::rune_icon)?; + module + .associated_function("keywords", Item::rune_keywords)?; + + // Register logging functions module.function("log_info", log_info).build()?; module.function("log_debug", log_debug).build()?; module.function("log_warn", log_warn).build()?; diff --git a/crates/owlry-rune/src/loader.rs b/crates/owlry-rune/src/loader.rs index 9334385..ce7ab9d 100644 --- a/crates/owlry-rune/src/loader.rs +++ b/crates/owlry-rune/src/loader.rs @@ -92,16 +92,37 @@ impl LoadedPlugin { self.registrations.iter().any(|r| r.name == name) } - /// Refresh a static provider (stub for now) + /// Refresh a static provider by calling the Rune `refresh()` function pub fn refresh_provider(&mut self, _name: &str) -> Result, String> { - // TODO: Implement provider refresh by calling Rune function - Ok(Vec::new()) + let mut vm = create_vm(&self.context, self.unit.clone()) + .map_err(|e| format!("Failed to create VM: {}", e))?; + + let output = vm + .call(rune::Hash::type_hash(["refresh"]), ()) + .map_err(|e| format!("refresh() call failed: {}", e))?; + + let items: Vec = rune::from_value(output) + .map_err(|e| format!("Failed to parse refresh() result: {}", e))?; + + Ok(items.iter().map(|i| i.to_plugin_item()).collect()) } - /// Query a dynamic provider (stub for now) - pub fn query_provider(&mut self, _name: &str, _query: &str) -> Result, String> { - // TODO: Implement provider query by calling Rune function - Ok(Vec::new()) + /// Query a dynamic provider by calling the Rune `query(q)` function + pub fn query_provider(&mut self, _name: &str, query: &str) -> Result, String> { + let mut vm = create_vm(&self.context, self.unit.clone()) + .map_err(|e| format!("Failed to create VM: {}", e))?; + + let output = vm + .call( + rune::Hash::type_hash(["query"]), + (query.to_string(),), + ) + .map_err(|e| format!("query() call failed: {}", e))?; + + let items: Vec = rune::from_value(output) + .map_err(|e| format!("Failed to parse query() result: {}", e))?; + + Ok(items.iter().map(|i| i.to_plugin_item()).collect()) } }