use std::collections::BTreeMap;
use schemars::JsonSchema as DeriveJsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
pub struct Pipeline {
pub version: String,
#[serde(default)]
pub env: Option<BTreeMap<String, String>>,
#[serde(default)]
pub default_image: Option<String>,
pub steps: Vec<Step>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Step {
Command(Box<CommandStep>),
Wait(WaitStep),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
pub struct CommandStep {
pub key: String,
#[serde(default)]
pub label: Option<String>,
pub cmd: String,
#[serde(default)]
pub builds_in: Option<String>,
#[serde(default)]
pub image: Option<String>,
#[serde(default)]
pub env: Option<BTreeMap<String, String>>,
#[serde(default)]
pub timeout_seconds: Option<u32>,
#[serde(default)]
pub cache: Option<Cache>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub runner: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub runner_args: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
pub struct WaitStep {
#[serde(default)]
pub continue_on_failure: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
pub struct Cache {
pub policy: String,
#[serde(default)]
pub key: Option<String>,
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod tests {
use super::*;
#[test]
fn parses_step_with_runner() {
let json = br#"{
"version": "0",
"steps": [
{"type": "command", "key": "a", "cmd": "echo a"},
{"type": "command", "key": "b", "cmd": "freestyle run",
"runner": "freestyle", "runner_args": {"region": "us"}}
]
}"#;
let p: Pipeline = serde_json::from_slice(json).unwrap();
let Step::Command(b) = &p.steps[1] else {
panic!("expected command")
};
assert_eq!(b.runner.as_deref(), Some("freestyle"));
assert_eq!(b.runner_args.as_ref().unwrap()["region"], "us");
}
#[test]
fn parses_legacy_step_without_runner() {
let json = br#"{
"version": "0",
"steps": [{"type": "command", "key": "a", "cmd": "echo a"}]
}"#;
let p: Pipeline = serde_json::from_slice(json).unwrap();
let Step::Command(a) = &p.steps[0] else {
panic!("expected command")
};
assert!(a.runner.is_none());
assert!(a.runner_args.is_none());
}
}