pinakes-core: update remaining modules and tests
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I9e0ff5ea33a5cf697473423e88f167ce6a6a6964
This commit is contained in:
parent
c8425a4c34
commit
3d9f8933d2
44 changed files with 1207 additions and 578 deletions
|
|
@ -97,6 +97,11 @@ impl From<crate::config::PluginsConfig> for PluginManagerConfig {
|
|||
|
||||
impl PluginManager {
|
||||
/// Create a new plugin manager
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the data or cache directories cannot be created, or
|
||||
/// if the WASM runtime cannot be initialized.
|
||||
pub fn new(
|
||||
data_dir: PathBuf,
|
||||
cache_dir: PathBuf,
|
||||
|
|
@ -123,10 +128,14 @@ impl PluginManager {
|
|||
}
|
||||
|
||||
/// Discover and load all plugins from configured directories
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if plugin discovery fails.
|
||||
pub async fn discover_and_load_all(&self) -> Result<Vec<String>> {
|
||||
info!("Discovering plugins from {:?}", self.config.plugin_dirs);
|
||||
|
||||
let manifests = self.loader.discover_plugins().await?;
|
||||
let manifests = self.loader.discover_plugins()?;
|
||||
let mut loaded_plugins = Vec::new();
|
||||
|
||||
for manifest in manifests {
|
||||
|
|
@ -145,6 +154,12 @@ impl PluginManager {
|
|||
}
|
||||
|
||||
/// Load a plugin from a manifest file
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the plugin ID is invalid, capability validation
|
||||
/// fails, the WASM binary cannot be loaded, or the plugin cannot be
|
||||
/// registered.
|
||||
async fn load_plugin_from_manifest(
|
||||
&self,
|
||||
manifest: &pinakes_plugin_api::PluginManifest,
|
||||
|
|
@ -156,7 +171,7 @@ impl PluginManager {
|
|||
|| plugin_id.contains('\\')
|
||||
|| plugin_id.contains("..")
|
||||
{
|
||||
return Err(anyhow::anyhow!("Invalid plugin ID: {}", plugin_id));
|
||||
return Err(anyhow::anyhow!("Invalid plugin ID: {plugin_id}"));
|
||||
}
|
||||
|
||||
// Check if already loaded
|
||||
|
|
@ -202,7 +217,7 @@ impl PluginManager {
|
|||
|
||||
// Load WASM binary
|
||||
let wasm_path = self.loader.resolve_wasm_path(manifest)?;
|
||||
let wasm_plugin = self.runtime.load_plugin(&wasm_path, context).await?;
|
||||
let wasm_plugin = self.runtime.load_plugin(&wasm_path, context)?;
|
||||
|
||||
// Initialize plugin
|
||||
let init_succeeded = match wasm_plugin
|
||||
|
|
@ -246,13 +261,20 @@ impl PluginManager {
|
|||
enabled: init_succeeded,
|
||||
};
|
||||
|
||||
let mut registry = self.registry.write().await;
|
||||
registry.register(registered)?;
|
||||
{
|
||||
let mut registry = self.registry.write().await;
|
||||
registry.register(registered)?;
|
||||
}
|
||||
|
||||
Ok(plugin_id)
|
||||
}
|
||||
|
||||
/// Install a plugin from a file or URL
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the plugin cannot be downloaded, the manifest cannot
|
||||
/// be read, or the plugin cannot be loaded.
|
||||
pub async fn install_plugin(&self, source: &str) -> Result<String> {
|
||||
info!("Installing plugin from: {}", source);
|
||||
|
||||
|
|
@ -276,13 +298,18 @@ impl PluginManager {
|
|||
}
|
||||
|
||||
/// Uninstall a plugin
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the plugin ID is invalid, the plugin cannot be shut
|
||||
/// down, cannot be unregistered, or its data directories cannot be removed.
|
||||
pub async fn uninstall_plugin(&self, plugin_id: &str) -> Result<()> {
|
||||
// Validate plugin_id to prevent path traversal
|
||||
if plugin_id.contains('/')
|
||||
|| plugin_id.contains('\\')
|
||||
|| plugin_id.contains("..")
|
||||
{
|
||||
return Err(anyhow::anyhow!("Invalid plugin ID: {}", plugin_id));
|
||||
return Err(anyhow::anyhow!("Invalid plugin ID: {plugin_id}"));
|
||||
}
|
||||
|
||||
info!("Uninstalling plugin: {}", plugin_id);
|
||||
|
|
@ -291,8 +318,10 @@ impl PluginManager {
|
|||
self.shutdown_plugin(plugin_id).await?;
|
||||
|
||||
// Remove from registry
|
||||
let mut registry = self.registry.write().await;
|
||||
registry.unregister(plugin_id)?;
|
||||
{
|
||||
let mut registry = self.registry.write().await;
|
||||
registry.unregister(plugin_id)?;
|
||||
}
|
||||
|
||||
// Remove plugin data and cache
|
||||
let plugin_data_dir = self.data_dir.join(plugin_id);
|
||||
|
|
@ -309,37 +338,55 @@ impl PluginManager {
|
|||
}
|
||||
|
||||
/// Enable a plugin
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the plugin ID is not found in the registry.
|
||||
pub async fn enable_plugin(&self, plugin_id: &str) -> Result<()> {
|
||||
let mut registry = self.registry.write().await;
|
||||
registry.enable(plugin_id)
|
||||
}
|
||||
|
||||
/// Disable a plugin
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the plugin ID is not found in the registry.
|
||||
pub async fn disable_plugin(&self, plugin_id: &str) -> Result<()> {
|
||||
let mut registry = self.registry.write().await;
|
||||
registry.disable(plugin_id)
|
||||
}
|
||||
|
||||
/// Shutdown a specific plugin
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the plugin ID is not found in the registry.
|
||||
pub async fn shutdown_plugin(&self, plugin_id: &str) -> Result<()> {
|
||||
debug!("Shutting down plugin: {}", plugin_id);
|
||||
|
||||
let registry = self.registry.read().await;
|
||||
if let Some(plugin) = registry.get(plugin_id) {
|
||||
plugin.wasm_plugin.call_function("shutdown", &[]).await.ok();
|
||||
let _ = plugin.wasm_plugin.call_function("shutdown", &[]).await;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow::anyhow!("Plugin not found: {}", plugin_id))
|
||||
Err(anyhow::anyhow!("Plugin not found: {plugin_id}"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Shutdown all plugins
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function always returns `Ok(())`. Individual plugin shutdown errors
|
||||
/// are logged but do not cause the overall operation to fail.
|
||||
pub async fn shutdown_all(&self) -> Result<()> {
|
||||
info!("Shutting down all plugins");
|
||||
|
||||
let registry = self.registry.read().await;
|
||||
let plugin_ids: Vec<String> =
|
||||
registry.list_all().iter().map(|p| p.id.clone()).collect();
|
||||
let plugin_ids: Vec<String> = {
|
||||
let registry = self.registry.read().await;
|
||||
registry.list_all().iter().map(|p| p.id.clone()).collect()
|
||||
};
|
||||
|
||||
for plugin_id in plugin_ids {
|
||||
if let Err(e) = self.shutdown_plugin(&plugin_id).await {
|
||||
|
|
@ -373,6 +420,11 @@ impl PluginManager {
|
|||
}
|
||||
|
||||
/// Reload a plugin (for hot-reload during development)
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if hot-reload is disabled, the plugin is not found, it
|
||||
/// cannot be shut down, or the reloaded plugin cannot be registered.
|
||||
pub async fn reload_plugin(&self, plugin_id: &str) -> Result<()> {
|
||||
if !self.config.enable_hot_reload {
|
||||
return Err(anyhow::anyhow!("Hot-reload is disabled"));
|
||||
|
|
@ -387,15 +439,21 @@ impl PluginManager {
|
|||
let plugin = registry
|
||||
.get(plugin_id)
|
||||
.ok_or_else(|| anyhow::anyhow!("Plugin not found"))?;
|
||||
if let Some(ref manifest_path) = plugin.manifest_path {
|
||||
pinakes_plugin_api::PluginManifest::from_file(manifest_path)
|
||||
.unwrap_or_else(|e| {
|
||||
warn!("Failed to re-read manifest from disk, using cached: {}", e);
|
||||
plugin.manifest.clone()
|
||||
})
|
||||
} else {
|
||||
plugin.manifest.clone()
|
||||
}
|
||||
let manifest = plugin.manifest_path.as_ref().map_or_else(
|
||||
|| plugin.manifest.clone(),
|
||||
|manifest_path| {
|
||||
pinakes_plugin_api::PluginManifest::from_file(manifest_path)
|
||||
.unwrap_or_else(|e| {
|
||||
warn!(
|
||||
"Failed to re-read manifest from disk, using cached: {}",
|
||||
e
|
||||
);
|
||||
plugin.manifest.clone()
|
||||
})
|
||||
},
|
||||
);
|
||||
drop(registry);
|
||||
manifest
|
||||
};
|
||||
|
||||
// Shutdown and unload current version
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue