restflow_core/models/
execution.rs

1use serde::{Deserialize, Serialize};
2use ts_rs::TS;
3
4/// Execution status
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS)]
6#[ts(export)]
7pub enum ExecutionStatus {
8    Running,
9    Completed,
10    Failed,
11}
12
13/// Execution summary - used for execution history list
14#[derive(Debug, Clone, Serialize, Deserialize, TS)]
15#[ts(export)]
16pub struct ExecutionSummary {
17    /// Execution ID
18    pub execution_id: String,
19    /// Workflow ID
20    pub workflow_id: String,
21    /// Execution status
22    pub status: ExecutionStatus,
23    /// Start time (millisecond timestamp)
24    pub started_at: i64,
25    /// Completion time (millisecond timestamp)
26    pub completed_at: Option<i64>,
27    /// Total number of tasks
28    pub total_tasks: usize,
29    /// Number of completed tasks
30    pub completed_tasks: usize,
31    /// Number of failed tasks
32    pub failed_tasks: usize,
33}
34
35impl ExecutionSummary {
36    /// Create ExecutionSummary from Task list
37    pub fn from_tasks(
38        execution_id: String,
39        workflow_id: String,
40        tasks: &[crate::models::Task],
41    ) -> Self {
42        if tasks.is_empty() {
43            return Self {
44                execution_id,
45                workflow_id,
46                status: ExecutionStatus::Running,
47                started_at: chrono::Utc::now().timestamp_millis(),
48                completed_at: None,
49                total_tasks: 0,
50                completed_tasks: 0,
51                failed_tasks: 0,
52            };
53        }
54
55        let total_tasks = tasks.len();
56        let completed_tasks = tasks
57            .iter()
58            .filter(|t| t.status == crate::models::TaskStatus::Completed)
59            .count();
60        let failed_tasks = tasks
61            .iter()
62            .filter(|t| t.status == crate::models::TaskStatus::Failed)
63            .count();
64        let running_tasks = tasks
65            .iter()
66            .filter(|t| t.status == crate::models::TaskStatus::Running)
67            .count();
68
69        // Calculate execution status
70        let status = if failed_tasks > 0
71            && running_tasks == 0
72            && (completed_tasks + failed_tasks == total_tasks)
73        {
74            ExecutionStatus::Failed
75        } else if completed_tasks == total_tasks {
76            ExecutionStatus::Completed
77        } else {
78            ExecutionStatus::Running
79        };
80
81        // Find earliest start time
82        let started_at = tasks
83            .iter()
84            .filter_map(|t| t.started_at.or(Some(t.created_at)))
85            .min()
86            .unwrap_or_else(|| chrono::Utc::now().timestamp_millis());
87
88        // Find latest completion time (if all tasks are done)
89        let completed_at = if status == ExecutionStatus::Running {
90            None
91        } else {
92            tasks.iter().filter_map(|t| t.completed_at).max()
93        };
94
95        Self {
96            execution_id,
97            workflow_id,
98            status,
99            started_at,
100            completed_at,
101            total_tasks,
102            completed_tasks,
103            failed_tasks,
104        }
105    }
106}
107
108/// Paginated execution history response
109#[derive(Debug, Clone, Serialize, Deserialize, TS)]
110#[ts(export)]
111pub struct ExecutionHistoryPage {
112    pub items: Vec<ExecutionSummary>,
113    pub total: usize,
114    pub page: usize,
115    pub page_size: usize,
116    pub total_pages: usize,
117}