fix: align Lua ProviderInfo ABI, implement Rune Item type and refresh/query
This commit is contained in:
@@ -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<RString>,
|
||||
/// 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<RString>,
|
||||
}
|
||||
|
||||
/// 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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String>) -> 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<Vec<ProviderRegistration>> = Mutex::new(Vec::new
|
||||
pub fn module() -> Result<Module, ContextError> {
|
||||
let mut module = Module::with_crate("owlry")?;
|
||||
|
||||
// Register logging functions using builder pattern
|
||||
// Register Item type with constructor and builder methods
|
||||
module.ty::<Item>()?;
|
||||
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()?;
|
||||
|
||||
@@ -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<Vec<PluginItem>, 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<crate::api::Item> = 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<Vec<PluginItem>, 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<Vec<PluginItem>, 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<crate::api::Item> = rune::from_value(output)
|
||||
.map_err(|e| format!("Failed to parse query() result: {}", e))?;
|
||||
|
||||
Ok(items.iter().map(|i| i.to_plugin_item()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user