use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::{ExtensionMap, Metadata};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")]
pub struct QualityGate {
pub id: String,
pub purpose: String,
pub criteria: Vec<QualityCriterion>,
pub on_success: GateOutcome,
pub on_failure: GateOutcome,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub placement: Option<GatePlacement>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
#[serde(default, flatten)]
pub extensions: ExtensionMap,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")]
pub struct QualityCriterion {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none", rename = "type")]
pub criterion_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub contract_ref: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expression: Option<String>,
#[serde(default, flatten)]
pub extensions: ExtensionMap,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GateOutcome {
Abort,
Retry,
AlternatePath,
RequireApproval,
EmitDiagnostics,
Continue,
Extension(String),
}
impl GateOutcome {
pub fn as_str(&self) -> &str {
match self {
Self::Abort => "abort",
Self::Retry => "retry",
Self::AlternatePath => "alternatePath",
Self::RequireApproval => "requireApproval",
Self::EmitDiagnostics => "emitDiagnostics",
Self::Continue => "continue",
Self::Extension(value) => value.as_str(),
}
}
}
impl Serialize for GateOutcome {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for GateOutcome {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let value = String::deserialize(deserializer)?;
Ok(match value.as_str() {
"abort" => Self::Abort,
"retry" => Self::Retry,
"alternatePath" => Self::AlternatePath,
"requireApproval" => Self::RequireApproval,
"emitDiagnostics" => Self::EmitDiagnostics,
"continue" => Self::Continue,
other => Self::Extension(other.to_owned()),
})
}
}
#[cfg(feature = "jsonschema")]
impl schemars::JsonSchema for GateOutcome {
fn schema_name() -> String {
"GateOutcome".to_owned()
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
String::json_schema(gen)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")]
pub struct GatePlacement {
pub kind: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub step_id: Option<String>,
}
impl GatePlacement {
pub fn targets_step(&self) -> bool {
let kind = self.kind.trim();
kind.eq_ignore_ascii_case("beforeStep") || kind.eq_ignore_ascii_case("afterStep")
}
}