use regex::Regex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use crate::cook::workflow::on_failure::OnFailureConfig;
use crate::cook::workflow::validation::ValidationConfig;
use super::types::{deserialize_capture_output, CaptureOutput};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HandlerStep {
pub name: String,
#[serde(default)]
pub attributes: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct WorkflowStep {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub claude: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shell: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub test: Option<crate::config::command::TestCommand>,
#[serde(skip_serializing_if = "Option::is_none")]
pub foreach: Option<crate::config::command::ForeachConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub write_file: Option<crate::config::command::WriteFileConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub command: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub handler: Option<HandlerStep>,
#[serde(skip_serializing_if = "Option::is_none")]
pub capture: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub capture_format: Option<super::super::variables::CaptureFormat>,
#[serde(default)]
pub capture_streams: super::super::variables::CaptureStreams,
#[serde(skip_serializing_if = "Option::is_none")]
pub output_file: Option<std::path::PathBuf>,
#[serde(default, deserialize_with = "deserialize_capture_output")]
pub capture_output: CaptureOutput,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub working_dir: Option<PathBuf>,
#[serde(default)]
pub env: HashMap<String, String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub on_failure: Option<OnFailureConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub retry: Option<crate::cook::retry_v2::RetryConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub on_success: Option<Box<WorkflowStep>>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub on_exit_code: HashMap<i32, Box<WorkflowStep>>,
#[serde(default = "default_commit_required")]
pub commit_required: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub commit_config: Option<crate::cook::commit_tracker::CommitConfig>,
#[serde(default)]
#[serde(skip_serializing_if = "is_false")]
pub auto_commit: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub validate: Option<ValidationConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub step_validate: Option<super::super::step_validation::StepValidationSpec>,
#[serde(default)]
pub skip_validation: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub validation_timeout: Option<u64>,
#[serde(default)]
pub ignore_validation_failure: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub when: Option<String>,
}
pub(crate) fn default_commit_required() -> bool {
false
}
pub(crate) fn is_false(b: &bool) -> bool {
!b
}
pub(crate) fn compile_regex(pattern: &str) -> Option<Regex> {
match Regex::new(pattern) {
Ok(regex) => Some(regex),
Err(e) => {
eprintln!("Warning: Failed to compile regex '{}': {}", pattern, e);
None
}
}
}
#[derive(Debug, Clone)]
pub struct SensitivePatternConfig {
pub name_patterns: Vec<Regex>,
pub value_patterns: Vec<Regex>,
pub mask_string: String,
}
impl Default for SensitivePatternConfig {
fn default() -> Self {
Self {
name_patterns: [
r"(?i)(password|passwd|pwd)",
r"(?i)(token|api[_-]?key|secret)",
r"(?i)(auth|authorization|bearer)",
r"(?i)(private[_-]?key|ssh[_-]?key)",
r"(?i)(access[_-]?key|client[_-]?secret)",
]
.iter()
.filter_map(|p| compile_regex(p))
.collect(),
value_patterns: [
r"^(ghp_|gho_|ghu_|ghs_|ghr_|glpat-)",
r"^AKIA[0-9A-Z]{16}$",
r"^eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$",
r"^(Basic|Bearer)\s+[A-Za-z0-9+/=]+$",
]
.iter()
.filter_map(|p| compile_regex(p))
.collect(),
mask_string: "***REDACTED***".to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum WorkflowMode {
Sequential,
MapReduce,
}
#[derive(Debug, Clone)]
pub struct ExtendedWorkflowConfig {
pub name: String,
pub mode: WorkflowMode,
pub steps: Vec<WorkflowStep>,
pub setup_phase: Option<crate::cook::execution::SetupPhase>,
pub map_phase: Option<crate::cook::execution::MapPhase>,
pub reduce_phase: Option<crate::cook::execution::ReducePhase>,
pub max_iterations: u32,
pub iterate: bool,
pub retry_defaults: Option<crate::cook::retry_v2::RetryConfig>,
pub environment: Option<crate::cook::environment::EnvironmentConfig>,
}