Skip to main content

ao_core/
parity_plugin_registry.rs

1//! TS plugin registry (ported from `packages/core/src/plugin-registry.ts`).
2//!
3//! Parity status: test-only.
4//!
5//! The real ao-rs plugin wiring lives at the workspace level (per-slot
6//! crates and explicit registration in `ao-cli`). This module only mirrors
7//! the TS registry shape for parity comparison. See
8//! `docs/ts-core-parity-report.md` → "Parity-only modules".
9
10use std::collections::HashMap;
11use std::sync::Arc;
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub struct PluginSlot(pub String);
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct PluginManifest {
18    pub name: String,
19    pub slot: PluginSlot,
20    pub description: String,
21    pub version: String,
22}
23
24#[derive(Clone)]
25pub struct PluginModule {
26    pub manifest: PluginManifest,
27    pub create: Arc<dyn Fn(Option<serde_json::Value>) -> serde_json::Value + Send + Sync>,
28}
29
30#[derive(Debug, Clone)]
31pub struct PluginInstance {
32    pub manifest: PluginManifest,
33    pub instance: serde_json::Value,
34    pub config: Option<serde_json::Value>,
35}
36
37#[derive(Default)]
38pub struct PluginRegistry {
39    by_slot: HashMap<PluginSlot, HashMap<String, PluginInstance>>,
40}
41
42impl PluginRegistry {
43    pub fn new() -> Self {
44        Self::default()
45    }
46
47    pub fn register(&mut self, plugin: PluginModule, config: Option<serde_json::Value>) {
48        let slot = plugin.manifest.slot.clone();
49        let name = plugin.manifest.name.clone();
50        let instance = (plugin.create)(config.clone());
51        let entry = PluginInstance {
52            manifest: plugin.manifest,
53            instance,
54            config,
55        };
56        self.by_slot.entry(slot).or_default().insert(name, entry);
57    }
58
59    pub fn get(&self, slot: &PluginSlot, name: &str) -> Option<&PluginInstance> {
60        self.by_slot.get(slot)?.get(name)
61    }
62
63    pub fn list(&self, slot: &PluginSlot) -> Vec<String> {
64        self.by_slot
65            .get(slot)
66            .map(|m| m.keys().cloned().collect::<Vec<_>>())
67            .unwrap_or_default()
68    }
69}