use crate::error::{GitLabError, Result};
use crate::models::{Job, JobStatus};
use gitlab::Gitlab;
use gitlab::api::{Query, projects::jobs, projects::pipelines::PipelineJobs};
pub struct JobListBuilder<'a> {
client: &'a Gitlab,
project: String,
pipeline_id: Option<u64>,
status: Option<JobStatus>,
limit: Option<usize>,
}
impl<'a> JobListBuilder<'a> {
pub(crate) fn new(client: &'a Gitlab, project: impl Into<String>) -> Self {
Self {
client,
project: project.into(),
pipeline_id: None,
status: None,
limit: None,
}
}
pub fn pipeline(mut self, pipeline_id: u64) -> Self {
self.pipeline_id = Some(pipeline_id);
self
}
pub fn status(mut self, status: JobStatus) -> Self {
self.status = Some(status);
self
}
pub fn limit(mut self, limit: usize) -> Self {
self.limit = Some(limit);
self
}
pub async fn list(self) -> Result<Vec<Job>> {
let jobs: Vec<Job> = if let Some(pipeline_id) = self.pipeline_id {
let endpoint = PipelineJobs::builder()
.project(&self.project)
.pipeline(pipeline_id)
.build()
.map_err(|e| GitLabError::api(format!("Failed to build jobs endpoint: {}", e)))?;
endpoint
.query(self.client)
.map_err(|e| GitLabError::api(format!("Failed to query jobs: {}", e)))?
} else {
vec![]
};
let mut filtered = jobs;
if let Some(status) = self.status {
filtered.retain(|j| j.status == status);
}
if let Some(limit) = self.limit {
filtered.truncate(limit);
}
Ok(filtered)
}
}
pub struct JobBuilder<'a> {
client: &'a Gitlab,
project: String,
job_id: u64,
}
impl<'a> JobBuilder<'a> {
pub(crate) fn new(client: &'a Gitlab, project: impl Into<String>, job_id: u64) -> Self {
Self {
client,
project: project.into(),
job_id,
}
}
pub async fn get(self) -> Result<Job> {
let endpoint = jobs::Job::builder()
.project(&self.project)
.job(self.job_id)
.build()
.map_err(|e| GitLabError::api(format!("Failed to build job endpoint: {}", e)))?;
endpoint.query(self.client).map_err(|e| match e {
gitlab::api::ApiError::GitlabService { status, .. } if status.as_u16() == 404 => {
GitLabError::not_found("job", self.job_id)
}
_ => GitLabError::api(format!("Failed to get job: {}", e)),
})
}
pub async fn retry(self) -> Result<Job> {
let endpoint = jobs::RetryJob::builder()
.project(&self.project)
.job(self.job_id)
.build()
.map_err(|e| GitLabError::api(format!("Failed to build retry endpoint: {}", e)))?;
endpoint
.query(self.client)
.map_err(|e| GitLabError::api(format!("Failed to retry job: {}", e)))
}
pub async fn cancel(self) -> Result<Job> {
let endpoint = jobs::CancelJob::builder()
.project(&self.project)
.job(self.job_id)
.build()
.map_err(|e| GitLabError::api(format!("Failed to build cancel endpoint: {}", e)))?;
endpoint
.query(self.client)
.map_err(|e| GitLabError::api(format!("Failed to cancel job: {}", e)))
}
pub async fn logs(self) -> Result<String> {
let endpoint = jobs::JobTrace::builder()
.project(&self.project)
.job(self.job_id)
.build()
.map_err(|e| GitLabError::api(format!("Failed to build trace endpoint: {}", e)))?;
endpoint
.query(self.client)
.map_err(|e| GitLabError::api(format!("Failed to get job logs: {}", e)))
}
pub async fn artifacts(self) -> Result<Vec<u8>> {
Err(GitLabError::api("Artifact download not yet implemented"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_job_list_builder() {
fn _compile_test(client: &Gitlab) {
let _builder = JobListBuilder::new(client, "project")
.pipeline(123)
.status(JobStatus::Failed)
.limit(10);
}
}
#[test]
fn test_job_builder() {
fn _compile_test(client: &Gitlab) {
let _builder = JobBuilder::new(client, "project", 456);
}
}
}