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}