use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::context::Context;
use crate::errors::ModuleError;
use crate::module::ModuleAnnotations;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApprovalRequest {
pub module_id: String,
pub arguments: serde_json::Value,
#[serde(skip)]
pub context: Option<Context<serde_json::Value>>,
#[serde(default)]
pub annotations: ModuleAnnotations,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default)]
pub tags: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApprovalResult {
pub status: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub approved_by: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub approval_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
#[async_trait]
pub trait ApprovalHandler: Send + Sync + std::fmt::Debug {
async fn request_approval(
&self,
request: &ApprovalRequest,
) -> Result<ApprovalResult, ModuleError>;
async fn check_approval(&self, approval_id: &str) -> Result<ApprovalResult, ModuleError>;
}
#[derive(Debug, Clone)]
pub struct AutoApproveHandler;
#[async_trait]
impl ApprovalHandler for AutoApproveHandler {
async fn request_approval(
&self,
_request: &ApprovalRequest,
) -> Result<ApprovalResult, ModuleError> {
Ok(ApprovalResult {
status: "approved".to_string(),
approved_by: Some("auto".to_string()),
reason: None,
approval_id: None,
metadata: None,
})
}
async fn check_approval(&self, _approval_id: &str) -> Result<ApprovalResult, ModuleError> {
Ok(ApprovalResult {
status: "approved".to_string(),
approved_by: Some("auto".to_string()),
reason: None,
approval_id: None,
metadata: None,
})
}
}
pub struct CallbackApprovalHandler {
callback: Box<dyn Fn(&ApprovalRequest) -> ApprovalResult + Send + Sync>,
}
impl CallbackApprovalHandler {
pub fn new(
callback: impl Fn(&ApprovalRequest) -> ApprovalResult + Send + Sync + 'static,
) -> Self {
Self {
callback: Box::new(callback),
}
}
}
impl std::fmt::Debug for CallbackApprovalHandler {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CallbackApprovalHandler")
.field("callback", &"<closure>")
.finish()
}
}
#[async_trait]
impl ApprovalHandler for CallbackApprovalHandler {
async fn request_approval(
&self,
request: &ApprovalRequest,
) -> Result<ApprovalResult, ModuleError> {
Ok((self.callback)(request))
}
async fn check_approval(&self, _approval_id: &str) -> Result<ApprovalResult, ModuleError> {
Ok(ApprovalResult {
status: "rejected".to_string(),
approved_by: None,
reason: Some("Phase B not supported by callback handler".to_string()),
approval_id: None,
metadata: None,
})
}
}
#[derive(Debug, Clone)]
pub struct AlwaysDenyHandler;
#[async_trait]
impl ApprovalHandler for AlwaysDenyHandler {
async fn request_approval(
&self,
_request: &ApprovalRequest,
) -> Result<ApprovalResult, ModuleError> {
Ok(ApprovalResult {
status: "rejected".to_string(),
approved_by: None,
reason: Some("Always denied".to_string()),
approval_id: None,
metadata: None,
})
}
async fn check_approval(&self, _approval_id: &str) -> Result<ApprovalResult, ModuleError> {
Ok(ApprovalResult {
status: "rejected".to_string(),
approved_by: None,
reason: Some("Always denied".to_string()),
approval_id: None,
metadata: None,
})
}
}