use std::sync::Arc;
use async_trait::async_trait;
use serde_json::Value;
use crate::error::SkillError;
use crate::handler::SkillHandler;
use crate::sink::SkillProgressSink;
#[non_exhaustive]
#[derive(Debug, Clone, Copy)]
pub struct GateContext<'a> {
pub skill_id: &'a str,
pub params: &'a Value,
pub output: &'a Value,
}
#[async_trait]
pub trait PreTerminalGate: Send + Sync {
async fn check(&self, ctx: GateContext<'_>) -> Result<(), SkillError>;
}
pub struct GatedHandler {
skill_id: String,
inner: Arc<dyn SkillHandler>,
gates: Vec<Arc<dyn PreTerminalGate>>,
}
impl GatedHandler {
pub fn new(skill_id: impl Into<String>, inner: Arc<dyn SkillHandler>) -> Self {
Self {
skill_id: skill_id.into(),
inner,
gates: Vec::new(),
}
}
pub fn gate(mut self, gate: Arc<dyn PreTerminalGate>) -> Self {
self.gates.push(gate);
self
}
}
#[async_trait]
impl SkillHandler for GatedHandler {
async fn run(&self, params: Value, sink: &dyn SkillProgressSink) -> Result<Value, SkillError> {
let output = self.inner.run(params.clone(), sink).await?;
for gate in &self.gates {
gate.check(GateContext {
skill_id: &self.skill_id,
params: ¶ms,
output: &output,
})
.await?;
}
Ok(output)
}
}
const _: fn() = || {
fn assert<T: ?Sized + PreTerminalGate>() {}
assert::<dyn PreTerminalGate>();
};