Skip to main content

cascade_agent/planning/
types.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::path::{Path, PathBuf};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Plan {
7    pub id: String,
8    pub task_id: String,
9    pub title: String,
10    pub steps: Vec<PlanStep>,
11    pub status: PlanStatus,
12    pub created_at: DateTime<Utc>,
13    pub updated_at: DateTime<Utc>,
14    #[serde(skip)]
15    pub file_path: PathBuf,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub(crate) struct PlanData {
20    id: String,
21    task_id: String,
22    title: String,
23    status: PlanStatus,
24    created_at: String,
25    updated_at: String,
26    steps: Vec<PlanStep>,
27}
28
29impl PlanData {
30    pub(crate) fn from_plan(plan: &Plan) -> Self {
31        Self {
32            id: plan.id.clone(),
33            task_id: plan.task_id.clone(),
34            title: plan.title.clone(),
35            status: plan.status.clone(),
36            created_at: plan.created_at.to_rfc3339(),
37            updated_at: plan.updated_at.to_rfc3339(),
38            steps: plan.steps.clone(),
39        }
40    }
41
42    pub(crate) fn to_plan(&self, file_path: &Path) -> crate::error::Result<Plan> {
43        Ok(Plan {
44            id: self.id.clone(),
45            task_id: self.task_id.clone(),
46            title: self.title.clone(),
47            status: self.status.clone(),
48            created_at: DateTime::parse_from_rfc3339(&self.created_at)
49                .map(|dt| dt.with_timezone(&Utc))
50                .map_err(|e| {
51                    crate::error::AgentError::ConfigError(format!("Invalid created_at: {}", e))
52                })?,
53            updated_at: DateTime::parse_from_rfc3339(&self.updated_at)
54                .map(|dt| dt.with_timezone(&Utc))
55                .map_err(|e| {
56                    crate::error::AgentError::ConfigError(format!("Invalid updated_at: {}", e))
57                })?,
58            steps: self.steps.clone(),
59            file_path: file_path.to_path_buf(),
60        })
61    }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct PlanStep {
66    pub number: usize,
67    pub description: String,
68    pub status: StepStatus,
69    pub result: Option<String>,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
73pub enum PlanStatus {
74    Draft,
75    PendingApproval,
76    Approved,
77    InProgress,
78    Completed,
79    Cancelled,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Copy)]
83pub enum StepStatus {
84    Pending,
85    InProgress,
86    Completed,
87    Failed,
88    Skipped,
89}
90
91impl Plan {
92    /// Create a new plan with the given steps
93    pub fn new(task_id: &str, title: &str, step_descriptions: Vec<String>) -> Self {
94        let steps: Vec<PlanStep> = step_descriptions
95            .into_iter()
96            .enumerate()
97            .map(|(i, desc)| PlanStep {
98                number: i + 1,
99                description: desc,
100                status: StepStatus::Pending,
101                result: None,
102            })
103            .collect();
104
105        Plan {
106            id: uuid::Uuid::new_v4().to_string(),
107            task_id: task_id.to_string(),
108            title: title.to_string(),
109            steps,
110            status: PlanStatus::Draft,
111            created_at: Utc::now(),
112            updated_at: Utc::now(),
113            file_path: PathBuf::new(), // Will be set by PlanManager
114        }
115    }
116
117    pub fn mark_step_in_progress(&mut self, step_num: usize) -> bool {
118        if let Some(step) = self.steps.iter_mut().find(|s| s.number == step_num) {
119            step.status = StepStatus::InProgress;
120            self.updated_at = Utc::now();
121            true
122        } else {
123            false
124        }
125    }
126
127    pub fn mark_step_completed(&mut self, step_num: usize, result: Option<String>) -> bool {
128        if let Some(step) = self.steps.iter_mut().find(|s| s.number == step_num) {
129            step.status = StepStatus::Completed;
130            step.result = result;
131            self.updated_at = Utc::now();
132            // If all steps completed, mark plan as completed
133            if self
134                .steps
135                .iter()
136                .all(|s| s.status == StepStatus::Completed || s.status == StepStatus::Skipped)
137            {
138                self.status = PlanStatus::Completed;
139            }
140            true
141        } else {
142            false
143        }
144    }
145
146    pub fn mark_step_failed(&mut self, step_num: usize, error: String) -> bool {
147        if let Some(step) = self.steps.iter_mut().find(|s| s.number == step_num) {
148            step.status = StepStatus::Failed;
149            step.result = Some(error);
150            self.updated_at = Utc::now();
151            true
152        } else {
153            false
154        }
155    }
156}