use clap::{Args, ValueEnum};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Args)]
pub struct CiArgs {
#[arg(long, short = 'p', value_name = "NAME")]
pub pipeline: Option<String>,
#[arg(long, value_name = "FORMAT", value_enum)]
pub export: Option<ExportFormat>,
#[arg(long, short = 'o', value_name = "PATH")]
pub output: Option<PathBuf>,
#[arg(long, value_name = "KEY=VALUE", value_delimiter = ',')]
pub filter_matrix: Vec<String>,
#[arg(long, short = 'j', default_value = "0", value_name = "N")]
pub jobs: usize,
#[arg(long, value_name = "REF")]
pub from: Option<String>,
#[arg(long)]
pub dry_run: bool,
#[arg(long, short = 'e', value_name = "NAME")]
pub environment: Option<String>,
#[arg(long, default_value = ".", value_name = "PATH")]
pub path: String,
#[arg(long, default_value = "cuenv", value_name = "PACKAGE")]
pub package: String,
}
impl CiArgs {
#[must_use]
pub fn pipeline_name(&self) -> &str {
self.pipeline.as_deref().unwrap_or("default")
}
#[must_use]
pub fn effective_jobs(&self) -> usize {
if self.jobs == 0 {
std::thread::available_parallelism()
.map(std::num::NonZero::get)
.unwrap_or(1)
} else {
self.jobs
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ExportFormat {
Buildkite,
Gitlab,
GithubActions,
Circleci,
}
impl ExportFormat {
#[must_use]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Buildkite => "buildkite",
Self::Gitlab => "gitlab",
Self::GithubActions => "github-actions",
Self::Circleci => "circleci",
}
}
#[must_use]
pub const fn extension(&self) -> &'static str {
match self {
Self::Buildkite | Self::Gitlab | Self::GithubActions | Self::Circleci => "yml",
}
}
}
impl std::fmt::Display for ExportFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pipeline_name_default() {
let args = CiArgs {
pipeline: None,
export: None,
output: None,
filter_matrix: vec![],
jobs: 0,
from: None,
dry_run: false,
environment: None,
path: ".".to_string(),
package: "cuenv".to_string(),
};
assert_eq!(args.pipeline_name(), "default");
}
#[test]
fn test_pipeline_name_explicit() {
let args = CiArgs {
pipeline: Some("release".to_string()),
export: None,
output: None,
filter_matrix: vec![],
jobs: 0,
from: None,
dry_run: false,
environment: None,
path: ".".to_string(),
package: "cuenv".to_string(),
};
assert_eq!(args.pipeline_name(), "release");
}
#[test]
fn test_effective_jobs_default() {
let args = CiArgs {
pipeline: None,
export: None,
output: None,
filter_matrix: vec![],
jobs: 0,
from: None,
dry_run: false,
environment: None,
path: ".".to_string(),
package: "cuenv".to_string(),
};
assert!(args.effective_jobs() >= 1);
}
#[test]
fn test_effective_jobs_explicit() {
let args = CiArgs {
pipeline: None,
export: None,
output: None,
filter_matrix: vec![],
jobs: 8,
from: None,
dry_run: false,
environment: None,
path: ".".to_string(),
package: "cuenv".to_string(),
};
assert_eq!(args.effective_jobs(), 8);
}
#[test]
fn test_export_format_as_str() {
assert_eq!(ExportFormat::Buildkite.as_str(), "buildkite");
assert_eq!(ExportFormat::Gitlab.as_str(), "gitlab");
assert_eq!(ExportFormat::GithubActions.as_str(), "github-actions");
assert_eq!(ExportFormat::Circleci.as_str(), "circleci");
}
#[test]
fn test_export_format_extension() {
assert_eq!(ExportFormat::Buildkite.extension(), "yml");
assert_eq!(ExportFormat::Gitlab.extension(), "yml");
assert_eq!(ExportFormat::GithubActions.extension(), "yml");
assert_eq!(ExportFormat::Circleci.extension(), "yml");
}
#[test]
fn test_export_format_display() {
assert_eq!(format!("{}", ExportFormat::Buildkite), "buildkite");
assert_eq!(format!("{}", ExportFormat::Gitlab), "gitlab");
}
}