use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Workflow {
pub workflow: WorkflowMeta,
#[serde(default)]
pub roles: HashMap<String, Role>,
#[serde(default)]
pub vars: HashMap<String, Variable>,
#[serde(default, rename = "step")]
pub steps: Vec<Step>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct WorkflowMeta {
pub name: String,
#[serde(default)]
pub description: String,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default)]
pub version: Option<String>,
#[serde(default)]
pub provider: Option<String>,
#[serde(default)]
pub model: Option<String>,
#[serde(default)]
pub resources: Vec<ResourceSpec>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResourceSpec {
Path(String),
Detailed {
path: String,
#[serde(default)]
name: Option<String>,
#[serde(default)]
description: Option<String>,
#[serde(default)]
required: bool,
},
}
impl ResourceSpec {
pub fn path(&self) -> &str {
match self {
ResourceSpec::Path(p) => p,
ResourceSpec::Detailed { path, .. } => path,
}
}
pub fn name(&self) -> Option<&str> {
match self {
ResourceSpec::Path(_) => None,
ResourceSpec::Detailed { name, .. } => name.as_deref(),
}
}
pub fn description(&self) -> Option<&str> {
match self {
ResourceSpec::Path(_) => None,
ResourceSpec::Detailed { description, .. } => description.as_deref(),
}
}
pub fn required(&self) -> bool {
match self {
ResourceSpec::Path(_) => false,
ResourceSpec::Detailed { required, .. } => *required,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Role {
#[serde(default)]
pub system_prompt: Option<String>,
#[serde(default)]
pub system_prompt_file: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Variable {
#[serde(rename = "type")]
pub var_type: VarType,
#[serde(default)]
pub default: Option<toml::Value>,
#[serde(default)]
pub default_file: Option<String>,
#[serde(default)]
pub description: String,
#[serde(default)]
pub from: Option<String>,
#[serde(default)]
pub required: bool,
#[serde(default)]
pub min_length: Option<u32>,
#[serde(default)]
pub max_length: Option<u32>,
#[serde(default)]
pub min: Option<f64>,
#[serde(default)]
pub max: Option<f64>,
#[serde(default)]
pub pattern: Option<String>,
#[serde(default)]
pub allowed_values: Option<Vec<toml::Value>>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VarType {
#[default]
String,
Number,
Bool,
Json,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Step {
pub name: String,
pub prompt: String,
#[serde(default)]
pub provider: Option<String>,
#[serde(default)]
pub model: Option<String>,
#[serde(default)]
pub depends_on: Vec<String>,
#[serde(default)]
pub inject_context: bool,
#[serde(default)]
pub condition: Option<String>,
#[serde(default)]
pub json: bool,
#[serde(default)]
pub json_schema: Option<String>,
#[serde(default)]
pub output: Option<String>,
#[serde(default)]
pub saves: HashMap<String, String>,
#[serde(default)]
pub timeout: Option<String>,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default)]
pub on_failure: Option<FailurePolicy>,
#[serde(default)]
pub max_retries: Option<u32>,
#[serde(default)]
pub next: Option<String>,
#[serde(default)]
pub system_prompt: Option<String>,
#[serde(default)]
pub role: Option<String>,
#[serde(default)]
pub max_turns: Option<u32>,
#[serde(default)]
pub description: String,
#[serde(default)]
pub interactive: bool,
#[serde(default)]
pub auto_approve: bool,
#[serde(default)]
pub root: Option<String>,
#[serde(default)]
pub add_dirs: Vec<String>,
#[serde(default)]
pub env: HashMap<String, String>,
#[serde(default)]
pub files: Vec<String>,
#[serde(default)]
pub resources: Vec<ResourceSpec>,
#[serde(default)]
pub context: Vec<String>,
#[serde(default)]
pub plan: Option<String>,
#[serde(default)]
pub mcp_config: Option<String>,
#[serde(default)]
pub worktree: bool,
#[serde(default)]
pub sandbox: Option<String>,
#[serde(default)]
pub race_group: Option<String>,
#[serde(default)]
pub retry_model: Option<String>,
#[serde(default)]
pub command: Option<StepCommand>,
#[serde(default)]
pub uncommitted: bool,
#[serde(default)]
pub base: Option<String>,
#[serde(default)]
pub commit: Option<String>,
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub plan_output: Option<String>,
#[serde(default)]
pub instructions: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum FailurePolicy {
#[default]
Fail,
Continue,
Retry,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum StepCommand {
Review,
Plan,
Pipe,
Collect,
Summary,
}
impl std::fmt::Display for VarType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
VarType::String => write!(f, "string"),
VarType::Number => write!(f, "number"),
VarType::Bool => write!(f, "bool"),
VarType::Json => write!(f, "json"),
}
}
}