oris_kernel/kernel/
interrupt_resolver.rs1use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8
9use crate::kernel::interrupt::{Interrupt, InterruptId};
10
11pub type ResolveResult = Result<serde_json::Value, InterruptResolverError>;
13
14#[async_trait]
16pub trait InterruptResolver: Send + Sync {
17 async fn resolve(&self, interrupt: &Interrupt) -> ResolveResult;
19}
20
21#[derive(Debug, thiserror::Error)]
23pub enum InterruptResolverError {
24 #[error("Resolver error: {0}")]
25 Resolver(String),
26 #[error("Interrupt not found: {0}")]
27 NotFound(InterruptId),
28 #[error("Resolution timeout")]
29 Timeout,
30 #[error("Resolution rejected")]
31 Rejected,
32}
33
34#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
36pub enum InterruptSource {
37 Ui,
39 Agent,
41 PolicyEngine,
43 Api,
45 Custom(String),
47}
48
49impl InterruptSource {
50 pub fn as_str(&self) -> &str {
51 match self {
52 InterruptSource::Ui => "ui",
53 InterruptSource::Agent => "agent",
54 InterruptSource::PolicyEngine => "policy_engine",
55 InterruptSource::Api => "api",
56 InterruptSource::Custom(s) => s.as_str(),
57 }
58 }
59}
60
61pub type InterruptHandlerFn = Box<dyn Fn(&Interrupt) -> ResolveResult + Send + Sync>;
63
64pub struct UnifiedInterruptResolver {
66 handlers: std::collections::HashMap<InterruptSource, InterruptHandlerFn>,
67}
68
69impl UnifiedInterruptResolver {
70 pub fn new() -> Self {
72 Self {
73 handlers: std::collections::HashMap::new(),
74 }
75 }
76
77 pub fn with_handler(mut self, source: InterruptSource, handler: InterruptHandlerFn) -> Self {
79 self.handlers.insert(source, handler);
80 self
81 }
82
83 pub fn resolve(&self, interrupt: &Interrupt) -> ResolveResult {
85 let source = extract_source(interrupt);
86
87 if let Some(handler) = self.handlers.get(&source) {
88 return handler(interrupt);
89 }
90
91 if let Some(handler) = self.handlers.get(&InterruptSource::Api) {
93 return handler(interrupt);
94 }
95
96 Err(InterruptResolverError::Resolver(format!(
97 "No handler for source: {}",
98 source.as_str()
99 )))
100 }
101}
102
103impl Default for UnifiedInterruptResolver {
104 fn default() -> Self {
105 Self::new()
106 }
107}
108
109#[async_trait]
110impl InterruptResolver for UnifiedInterruptResolver {
111 async fn resolve(&self, interrupt: &Interrupt) -> ResolveResult {
112 self.resolve(interrupt)
113 }
114}
115
116fn extract_source(_interrupt: &Interrupt) -> InterruptSource {
118 InterruptSource::Api
121}
122
123#[cfg(test)]
125pub fn create_test_resolver() -> UnifiedInterruptResolver {
126 let noop_handler = Box::new(|_: &Interrupt| Ok(serde_json::Value::Null));
127
128 UnifiedInterruptResolver::new()
129 .with_handler(InterruptSource::Ui, noop_handler.clone())
130 .with_handler(InterruptSource::Agent, noop_handler.clone())
131 .with_handler(InterruptSource::PolicyEngine, noop_handler.clone())
132 .with_handler(InterruptSource::Api, noop_handler)
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use crate::kernel::interrupt::InterruptKind;
139
140 #[test]
141 fn resolver_routes_by_source() {
142 let resolver = create_test_resolver();
143
144 let ui_interrupt = Interrupt::new(
145 "i1".into(),
146 "run-1".into(),
147 InterruptKind::HumanInTheLoop,
148 serde_json::json!({}),
149 );
150
151 let result = resolver.resolve(&ui_interrupt);
152 assert!(result.is_ok());
153 }
154}