lmrc_gitlab/models/
pipeline.rs

1//! Pipeline-related data models.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6/// Basic pipeline information returned from list operations.
7///
8/// This is a lighter version of [`Pipeline`] with fewer fields,
9/// typically used when listing multiple pipelines.
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11pub struct PipelineBasic {
12    /// Unique pipeline identifier
13    pub id: u64,
14
15    /// Git reference (branch or tag) the pipeline ran on
16    #[serde(rename = "ref")]
17    pub ref_name: String,
18
19    /// Current status of the pipeline
20    pub status: PipelineStatus,
21
22    /// Source that triggered the pipeline (push, web, api, etc.)
23    pub source: Option<String>,
24
25    /// When the pipeline was created
26    pub created_at: DateTime<Utc>,
27
28    /// When the pipeline was last updated
29    pub updated_at: DateTime<Utc>,
30
31    /// Web URL to view the pipeline in GitLab UI
32    pub web_url: String,
33}
34
35/// Detailed pipeline information.
36///
37/// This includes all fields from [`PipelineBasic`] plus additional
38/// details like duration, user information, and more.
39#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
40pub struct Pipeline {
41    /// Unique pipeline identifier
42    pub id: u64,
43
44    /// Internal ID (project-specific)
45    pub iid: Option<u64>,
46
47    /// Git reference (branch or tag) the pipeline ran on
48    #[serde(rename = "ref")]
49    pub ref_name: String,
50
51    /// Current status of the pipeline
52    pub status: PipelineStatus,
53
54    /// Source that triggered the pipeline
55    pub source: Option<String>,
56
57    /// Git commit SHA that the pipeline ran on
58    pub sha: String,
59
60    /// Short SHA (first 8 characters)
61    #[serde(default)]
62    pub short_sha: Option<String>,
63
64    /// When the pipeline was created
65    pub created_at: DateTime<Utc>,
66
67    /// When the pipeline was last updated
68    pub updated_at: DateTime<Utc>,
69
70    /// When the pipeline started running
71    pub started_at: Option<DateTime<Utc>>,
72
73    /// When the pipeline finished (success or failure)
74    pub finished_at: Option<DateTime<Utc>>,
75
76    /// Duration of the pipeline execution in seconds
77    pub duration: Option<f64>,
78
79    /// Time spent in queued state before execution
80    pub queued_duration: Option<f64>,
81
82    /// Web URL to view the pipeline in GitLab UI
83    pub web_url: String,
84
85    /// Coverage percentage (if coverage reporting is enabled)
86    pub coverage: Option<String>,
87
88    /// User who triggered the pipeline
89    #[serde(default)]
90    pub user: Option<PipelineUser>,
91
92    /// Whether this pipeline was triggered
93    #[serde(default)]
94    pub triggered: bool,
95}
96
97/// User information associated with a pipeline.
98#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
99pub struct PipelineUser {
100    /// User ID
101    pub id: u64,
102
103    /// Username
104    pub username: String,
105
106    /// Display name
107    pub name: String,
108
109    /// User state (active, blocked, etc.)
110    pub state: String,
111
112    /// URL to user's avatar image
113    pub avatar_url: Option<String>,
114
115    /// Web URL to user's profile
116    pub web_url: String,
117}
118
119/// Status of a pipeline.
120///
121/// Represents the various states a GitLab pipeline can be in during its lifecycle.
122#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
123#[serde(rename_all = "snake_case")]
124pub enum PipelineStatus {
125    /// Pipeline created but not yet running
126    Created,
127
128    /// Pipeline is waiting to be picked up
129    Pending,
130
131    /// Pipeline is preparing to run (downloading artifacts, etc.)
132    Preparing,
133
134    /// Pipeline is currently running
135    Running,
136
137    /// Pipeline completed successfully
138    Success,
139
140    /// Pipeline failed
141    Failed,
142
143    /// Pipeline was canceled
144    Canceled,
145
146    /// Pipeline was skipped
147    Skipped,
148
149    /// Pipeline is waiting for manual action
150    Manual,
151
152    /// Pipeline is scheduled to run
153    Scheduled,
154}
155
156impl PipelineStatus {
157    /// Returns `true` if the pipeline is in a terminal state (completed, won't change).
158    ///
159    /// # Examples
160    ///
161    /// ```
162    /// use lmrc_gitlab::models::PipelineStatus;
163    ///
164    /// assert!(PipelineStatus::Success.is_finished());
165    /// assert!(PipelineStatus::Failed.is_finished());
166    /// assert!(!PipelineStatus::Running.is_finished());
167    /// ```
168    pub fn is_finished(self) -> bool {
169        matches!(
170            self,
171            Self::Success | Self::Failed | Self::Canceled | Self::Skipped
172        )
173    }
174
175    /// Returns `true` if the pipeline is currently active (running or pending).
176    ///
177    /// # Examples
178    ///
179    /// ```
180    /// use lmrc_gitlab::models::PipelineStatus;
181    ///
182    /// assert!(PipelineStatus::Running.is_active());
183    /// assert!(PipelineStatus::Pending.is_active());
184    /// assert!(!PipelineStatus::Success.is_active());
185    /// ```
186    pub fn is_active(self) -> bool {
187        matches!(
188            self,
189            Self::Created | Self::Pending | Self::Preparing | Self::Running
190        )
191    }
192
193    /// Returns `true` if the pipeline succeeded.
194    ///
195    /// # Examples
196    ///
197    /// ```
198    /// use lmrc_gitlab::models::PipelineStatus;
199    ///
200    /// assert!(PipelineStatus::Success.is_successful());
201    /// assert!(!PipelineStatus::Failed.is_successful());
202    /// ```
203    pub fn is_successful(self) -> bool {
204        self == Self::Success
205    }
206
207    /// Returns `true` if the pipeline failed.
208    ///
209    /// # Examples
210    ///
211    /// ```
212    /// use lmrc_gitlab::models::PipelineStatus;
213    ///
214    /// assert!(PipelineStatus::Failed.is_failed());
215    /// assert!(!PipelineStatus::Success.is_failed());
216    /// ```
217    pub fn is_failed(self) -> bool {
218        self == Self::Failed
219    }
220}
221
222impl std::fmt::Display for PipelineStatus {
223    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224        match self {
225            Self::Created => write!(f, "created"),
226            Self::Pending => write!(f, "pending"),
227            Self::Preparing => write!(f, "preparing"),
228            Self::Running => write!(f, "running"),
229            Self::Success => write!(f, "success"),
230            Self::Failed => write!(f, "failed"),
231            Self::Canceled => write!(f, "canceled"),
232            Self::Skipped => write!(f, "skipped"),
233            Self::Manual => write!(f, "manual"),
234            Self::Scheduled => write!(f, "scheduled"),
235        }
236    }
237}
238
239#[cfg(test)]
240mod tests {
241    use super::*;
242
243    #[test]
244    fn test_pipeline_status_is_finished() {
245        assert!(PipelineStatus::Success.is_finished());
246        assert!(PipelineStatus::Failed.is_finished());
247        assert!(PipelineStatus::Canceled.is_finished());
248        assert!(PipelineStatus::Skipped.is_finished());
249
250        assert!(!PipelineStatus::Running.is_finished());
251        assert!(!PipelineStatus::Pending.is_finished());
252    }
253
254    #[test]
255    fn test_pipeline_status_is_active() {
256        assert!(PipelineStatus::Running.is_active());
257        assert!(PipelineStatus::Pending.is_active());
258        assert!(PipelineStatus::Created.is_active());
259        assert!(PipelineStatus::Preparing.is_active());
260
261        assert!(!PipelineStatus::Success.is_active());
262        assert!(!PipelineStatus::Failed.is_active());
263    }
264
265    #[test]
266    fn test_pipeline_status_display() {
267        assert_eq!(PipelineStatus::Success.to_string(), "success");
268        assert_eq!(PipelineStatus::Failed.to_string(), "failed");
269        assert_eq!(PipelineStatus::Running.to_string(), "running");
270    }
271
272    #[test]
273    fn test_pipeline_status_serialization() {
274        let status = PipelineStatus::Success;
275        let json = serde_json::to_string(&status).unwrap();
276        assert_eq!(json, "\"success\"");
277
278        let deserialized: PipelineStatus = serde_json::from_str(&json).unwrap();
279        assert_eq!(deserialized, PipelineStatus::Success);
280    }
281}