use std::collections::HashMap;
use std::sync::Arc;
use crate::dsl::ApprovalMode;
use crate::engine_error::EngineError;
use crate::traits::run_context::RunContext;
#[derive(Debug)]
pub enum GatePoll {
Approved(Option<String>),
Rejected(String),
Pending,
}
#[allow(dead_code)] pub struct GateParams {
pub gate_name: String,
pub prompt: Option<String>,
pub min_approvals: u32,
pub approval_mode: ApprovalMode,
pub options: HashMap<String, String>,
pub timeout_secs: u64,
pub as_identity: Option<String>,
pub step_id: String,
}
pub trait GateResolver: Send + Sync {
fn gate_type(&self) -> &str;
fn poll(
&self,
run_id: &str,
params: &GateParams,
ctx: &dyn RunContext,
) -> Result<GatePoll, EngineError>;
}
pub struct GateResolverRegistry {
resolvers: HashMap<String, Arc<dyn GateResolver>>,
}
impl GateResolverRegistry {
pub fn new() -> Self {
Self {
resolvers: HashMap::new(),
}
}
pub fn register<R: GateResolver + 'static>(&mut self, resolver: R) {
let gate_type = resolver.gate_type().to_string();
self.resolvers.insert(gate_type, Arc::new(resolver));
}
pub fn has_type(&self, gate_type: &str) -> bool {
self.resolvers.contains_key(gate_type)
}
pub fn registered_types(&self) -> Vec<String> {
let mut types: Vec<String> = self.resolvers.keys().cloned().collect();
types.sort();
types
}
}
impl Default for GateResolverRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockResolver {
gate_type: &'static str,
}
impl GateResolver for MockResolver {
fn gate_type(&self) -> &str {
self.gate_type
}
fn poll(
&self,
_run_id: &str,
_params: &GateParams,
_ctx: &dyn RunContext,
) -> Result<GatePoll, EngineError> {
Ok(GatePoll::Approved(None))
}
}
#[test]
fn register_and_has_type_roundtrip() {
let mut registry = GateResolverRegistry::new();
registry.register(MockResolver {
gate_type: "human_approval",
});
assert!(registry.has_type("human_approval"));
assert!(!registry.has_type("pr_checks"));
}
#[test]
fn missing_type_returns_false() {
let registry = GateResolverRegistry::new();
assert!(!registry.has_type("nonexistent"));
}
#[test]
fn registered_types_is_sorted() {
let mut registry = GateResolverRegistry::new();
registry.register(MockResolver {
gate_type: "pr_checks",
});
registry.register(MockResolver {
gate_type: "human_approval",
});
let types = registry.registered_types();
assert_eq!(types, vec!["human_approval", "pr_checks"]);
}
#[test]
fn default_registry_is_empty() {
let registry = GateResolverRegistry::default();
assert!(registry.registered_types().is_empty());
}
}