Skip to main content

ainl_runtime/adapters/
mod.rs

1//! Patch adapter registry and reference [`GraphPatchAdapter`] for GraphPatch-shaped procedural nodes.
2
3mod graph_patch;
4
5pub use graph_patch::{GraphPatchAdapter, GraphPatchHostDispatch};
6
7use std::collections::HashMap;
8
9use crate::engine::PatchDispatchContext;
10
11/// Trait for patch/tool adapters. Implement to give dispatched patches a host execution target.
12/// Register via [`crate::AinlRuntime::register_adapter`].
13///
14/// Label-keyed dispatch calls [`Self::execute_patch`] with a [`PatchDispatchContext`]. The default
15/// implementation delegates to [`Self::execute`] with the patch label and frame (back-compat for
16/// simple adapters). The reference [`GraphPatchAdapter`] overrides [`Self::execute_patch`] to emit a
17/// structured GraphPatch envelope (and optional host hook).
18pub trait PatchAdapter: Send + Sync {
19    /// Canonical registry key (often the procedural IR `label`, e.g. `bash` or `L_my_patch`).
20    fn name(&self) -> &str;
21
22    /// Legacy entrypoint; used by the default [`Self::execute_patch`].
23    fn execute(
24        &self,
25        label: &str,
26        frame: &HashMap<String, serde_json::Value>,
27    ) -> Result<serde_json::Value, String>;
28
29    /// Rich dispatch context (node id, procedural payload, frame). Override for GraphPatch-style hosts.
30    fn execute_patch(&self, ctx: &PatchDispatchContext<'_>) -> Result<serde_json::Value, String> {
31        self.execute(ctx.patch_label, ctx.frame)
32    }
33}
34
35#[derive(Default)]
36pub struct AdapterRegistry {
37    adapters: HashMap<String, Box<dyn PatchAdapter>>,
38}
39
40impl AdapterRegistry {
41    pub fn new() -> Self {
42        Self::default()
43    }
44
45    pub fn register(&mut self, adapter: impl PatchAdapter + 'static) {
46        self.adapters
47            .insert(adapter.name().to_string(), Box::new(adapter));
48    }
49
50    pub fn get(&self, name: &str) -> Option<&dyn PatchAdapter> {
51        self.adapters.get(name).map(|boxed| boxed.as_ref())
52    }
53
54    pub fn registered_names(&self) -> Vec<&str> {
55        self.adapters.keys().map(|s| s.as_str()).collect()
56    }
57
58    pub fn is_empty(&self) -> bool {
59        self.adapters.is_empty()
60    }
61}