use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Job {
pub id: u64,
pub name: String,
pub status: JobStatus,
pub stage: String,
#[serde(rename = "ref")]
pub ref_name: String,
#[serde(default)]
pub sha: Option<String>,
pub created_at: DateTime<Utc>,
pub started_at: Option<DateTime<Utc>>,
pub finished_at: Option<DateTime<Utc>>,
pub duration: Option<f64>,
pub queued_duration: Option<f64>,
pub web_url: String,
#[serde(default)]
pub user: Option<JobUser>,
#[serde(default)]
pub runner: Option<JobRunner>,
#[serde(default)]
pub allow_failure: bool,
#[serde(default)]
pub tag_list: Vec<String>,
#[serde(default)]
pub artifacts: Vec<Artifact>,
#[serde(default)]
pub coverage: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct JobUser {
pub id: u64,
pub username: String,
pub name: String,
pub state: String,
pub avatar_url: Option<String>,
pub web_url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct JobRunner {
pub id: u64,
pub description: String,
#[serde(default)]
pub active: bool,
#[serde(default)]
pub is_shared: bool,
#[serde(default)]
pub name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Artifact {
pub filename: String,
pub size: u64,
#[serde(default)]
pub file_type: Option<String>,
#[serde(default)]
pub file_format: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum JobStatus {
Created,
Pending,
Running,
Success,
Failed,
Canceled,
Skipped,
Manual,
Scheduled,
}
impl JobStatus {
pub fn is_finished(self) -> bool {
matches!(
self,
Self::Success | Self::Failed | Self::Canceled | Self::Skipped
)
}
pub fn is_active(self) -> bool {
matches!(self, Self::Created | Self::Pending | Self::Running)
}
pub fn is_successful(self) -> bool {
self == Self::Success
}
pub fn is_failed(self) -> bool {
self == Self::Failed
}
}
impl std::fmt::Display for JobStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Created => write!(f, "created"),
Self::Pending => write!(f, "pending"),
Self::Running => write!(f, "running"),
Self::Success => write!(f, "success"),
Self::Failed => write!(f, "failed"),
Self::Canceled => write!(f, "canceled"),
Self::Skipped => write!(f, "skipped"),
Self::Manual => write!(f, "manual"),
Self::Scheduled => write!(f, "scheduled"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_job_status_is_finished() {
assert!(JobStatus::Success.is_finished());
assert!(JobStatus::Failed.is_finished());
assert!(JobStatus::Canceled.is_finished());
assert!(JobStatus::Skipped.is_finished());
assert!(!JobStatus::Running.is_finished());
assert!(!JobStatus::Pending.is_finished());
}
#[test]
fn test_job_status_is_active() {
assert!(JobStatus::Running.is_active());
assert!(JobStatus::Pending.is_active());
assert!(JobStatus::Created.is_active());
assert!(!JobStatus::Success.is_active());
assert!(!JobStatus::Failed.is_active());
}
#[test]
fn test_job_status_display() {
assert_eq!(JobStatus::Success.to_string(), "success");
assert_eq!(JobStatus::Failed.to_string(), "failed");
assert_eq!(JobStatus::Running.to_string(), "running");
}
#[test]
fn test_job_status_serialization() {
let status = JobStatus::Success;
let json = serde_json::to_string(&status).unwrap();
assert_eq!(json, "\"success\"");
let deserialized: JobStatus = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, JobStatus::Success);
}
}