folk_core/
plugin_registry.rs1use anyhow::{Context, Result};
4use folk_api::{Plugin, PluginContext};
5use tracing::{error, info};
6
7pub struct PluginRegistry {
11 plugins: Vec<Box<dyn Plugin>>,
12}
13
14impl PluginRegistry {
15 pub fn new() -> Self {
16 Self {
17 plugins: Vec::new(),
18 }
19 }
20
21 pub fn register(&mut self, plugin: Box<dyn Plugin>) {
23 info!(plugin = plugin.name(), "registered");
24 self.plugins.push(plugin);
25 }
26
27 pub async fn boot_all(&mut self, ctx: &PluginContext) -> Result<()> {
30 for (booted_count, plugin) in self.plugins.iter_mut().enumerate() {
31 let name = plugin.name();
32 info!(plugin = name, "booting");
33 if let Err(e) = plugin.boot(ctx.clone()).await {
34 error!(plugin = name, error = ?e, "boot failed; rolling back");
35 self.shutdown_partial(booted_count).await;
36 return Err(e).with_context(|| format!("plugin '{name}' boot failed"));
37 }
38 }
39 Ok(())
40 }
41
42 pub async fn shutdown_all(&self) {
45 self.shutdown_partial(self.plugins.len()).await;
46 }
47
48 async fn shutdown_partial(&self, count: usize) {
49 for plugin in self.plugins.iter().take(count).rev() {
50 let name = plugin.name();
51 info!(plugin = name, "shutting down");
52 if let Err(e) = plugin.shutdown().await {
53 error!(plugin = name, error = ?e, "shutdown error (continuing)");
54 }
55 }
56 }
57
58 pub fn names(&self) -> Vec<&'static str> {
59 self.plugins.iter().map(|p| p.name()).collect()
60 }
61}
62
63impl Default for PluginRegistry {
64 fn default() -> Self {
65 Self::new()
66 }
67}