Skip to main content

orchestrator_config/config/
prehook.rs

1use serde::{Deserialize, Serialize};
2
3/// Step hook engine type
4#[derive(Debug, Clone, Serialize, Deserialize, Default)]
5#[serde(rename_all = "snake_case")]
6pub enum StepHookEngine {
7    /// Evaluate hooks with the CEL expression engine.
8    #[default]
9    Cel,
10}
11
12/// Prehook UI mode
13#[derive(Debug, Clone, Serialize, Deserialize)]
14#[serde(rename_all = "snake_case")]
15pub enum StepPrehookUiMode {
16    /// Use the visual builder representation.
17    Visual,
18    /// Use a raw CEL expression editor.
19    Cel,
20}
21
22/// Prehook UI configuration
23#[derive(Debug, Clone, Serialize, Deserialize, Default)]
24pub struct StepPrehookUiConfig {
25    /// Preferred UI editing mode.
26    #[serde(default, skip_serializing_if = "Option::is_none")]
27    pub mode: Option<StepPrehookUiMode>,
28    /// Optional preset identifier selected in the UI.
29    #[serde(default, skip_serializing_if = "Option::is_none")]
30    pub preset_id: Option<String>,
31    /// Serialized UI expression payload.
32    #[serde(default, skip_serializing_if = "Option::is_none")]
33    pub expr: Option<serde_json::Value>,
34}
35
36/// Step prehook configuration
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct StepPrehookConfig {
39    /// Expression engine used to evaluate the prehook.
40    #[serde(default)]
41    pub engine: StepHookEngine,
42    /// Expression that decides whether the step should run.
43    pub when: String,
44    /// Optional human-readable explanation shown when the hook matches.
45    #[serde(default)]
46    pub reason: Option<String>,
47    /// UI metadata used by manifest editors.
48    #[serde(default, skip_serializing_if = "Option::is_none")]
49    pub ui: Option<StepPrehookUiConfig>,
50    /// Enables extended context fields during evaluation.
51    #[serde(default)]
52    pub extended: bool,
53}
54
55/// Workflow finalize rule
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct WorkflowFinalizeRule {
58    /// Stable rule identifier.
59    pub id: String,
60    /// Expression engine used to evaluate the rule.
61    #[serde(default)]
62    pub engine: StepHookEngine,
63    /// Expression that decides whether the rule matches.
64    pub when: String,
65    /// Status written when the rule matches.
66    pub status: String,
67    /// Optional human-readable explanation for the outcome.
68    #[serde(default)]
69    pub reason: Option<String>,
70}
71
72/// Workflow finalize configuration
73#[derive(Debug, Clone, Serialize, Deserialize, Default)]
74pub struct WorkflowFinalizeConfig {
75    /// Ordered finalize rules evaluated after task-item execution.
76    #[serde(default)]
77    pub rules: Vec<WorkflowFinalizeRule>,
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn test_step_prehook_ui_config_default() {
86        let cfg = StepPrehookUiConfig::default();
87        assert!(cfg.mode.is_none());
88        assert!(cfg.preset_id.is_none());
89        assert!(cfg.expr.is_none());
90    }
91
92    #[test]
93    fn test_step_hook_engine_default() {
94        let engine = StepHookEngine::default();
95        assert!(matches!(engine, StepHookEngine::Cel));
96    }
97
98    #[test]
99    fn test_workflow_finalize_config_default_empty() {
100        let cfg = WorkflowFinalizeConfig::default();
101        assert!(cfg.rules.is_empty());
102    }
103
104    #[test]
105    fn test_workflow_finalize_config_serde_round_trip() {
106        let cfg = super::super::default_workflow_finalize_config();
107        let json = serde_json::to_string(&cfg).expect("workflow finalize config should serialize");
108        let cfg2: WorkflowFinalizeConfig =
109            serde_json::from_str(&json).expect("workflow finalize config should deserialize");
110        assert_eq!(cfg2.rules.len(), cfg.rules.len());
111        assert_eq!(cfg2.rules[0].id, cfg.rules[0].id);
112    }
113}