ainl_runtime/adapters/
graph_patch.rs1use std::sync::Arc;
6
7use serde_json::{json, Value};
8
9use super::PatchAdapter;
10use crate::engine::PatchDispatchContext;
11use ainl_memory::AinlNodeType;
12
13pub trait GraphPatchHostDispatch: Send + Sync {
15 fn on_patch_dispatch(&self, envelope: Value) -> Result<Value, String>;
16}
17
18pub struct GraphPatchAdapter {
21 host: Option<Arc<dyn GraphPatchHostDispatch>>,
22}
23
24impl GraphPatchAdapter {
25 pub const NAME: &'static str = "graph_patch";
26
27 pub fn new() -> Self {
28 Self { host: None }
29 }
30
31 pub fn with_host(host: Arc<dyn GraphPatchHostDispatch>) -> Self {
32 Self { host: Some(host) }
33 }
34}
35
36impl Default for GraphPatchAdapter {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42fn build_summary(ctx: &PatchDispatchContext<'_>) -> Result<Value, String> {
43 let proc = match &ctx.node.node_type {
44 AinlNodeType::Procedural { procedural } => procedural,
45 _ => {
46 return Err("graph_patch: PatchDispatchContext.node is not procedural".to_string());
47 }
48 };
49 for key in &proc.declared_reads {
50 if !ctx.frame.contains_key(key) {
51 return Err(format!(
52 "graph_patch: declared read {key:?} missing from frame (adapter safety check)"
53 ));
54 }
55 }
56 let mut frame_keys: Vec<String> = ctx.frame.keys().cloned().collect();
57 frame_keys.sort_unstable();
58 Ok(json!({
61 "label": ctx.patch_label,
62 "patch_version": proc.patch_version,
63 "frame_keys": frame_keys,
64 "graph_writes": [],
65 }))
66}
67
68impl PatchAdapter for GraphPatchAdapter {
69 fn name(&self) -> &str {
70 Self::NAME
71 }
72
73 fn execute_patch(&self, ctx: &PatchDispatchContext<'_>) -> Result<Value, String> {
74 let summary = build_summary(ctx)?;
75 if let Some(h) = &self.host {
76 h.on_patch_dispatch(summary)
77 } else {
78 Ok(summary)
79 }
80 }
81}