use clap::ValueEnum;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RulesFile {
pub version: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trailer_prefix: Option<TrailerPrefix>,
pub rules: Vec<Rule>,
pub default: Policy,
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(try_from = "String", into = "String")]
pub struct TrailerPrefix(String);
impl std::fmt::Debug for TrailerPrefix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.0, f)
}
}
impl Default for TrailerPrefix {
fn default() -> Self {
TrailerPrefix(crate::provenance::DEFAULT_PREFIX.to_owned())
}
}
impl TryFrom<String> for TrailerPrefix {
type Error = String;
fn try_from(value: String) -> std::result::Result<Self, Self::Error> {
match crate::provenance::validate_prefix(&value) {
Ok(()) => Ok(TrailerPrefix(value)),
Err(reason) => Err(format!(
"{value:?} cannot spell a git trailer key: {reason}"
)),
}
}
}
impl From<TrailerPrefix> for String {
fn from(prefix: TrailerPrefix) -> Self {
prefix.0
}
}
impl std::ops::Deref for TrailerPrefix {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for TrailerPrefix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Rule {
#[serde(rename = "match")]
pub r#match: RuleMatch,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub publication: Option<MergePolicy>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub approvals: Option<Approvals>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub gate: Option<Gate>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RuleMatch {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub host: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub owner: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Policy {
pub publication: MergePolicy,
pub approvals: Approvals,
pub gate: Gate,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ValueEnum)]
#[serde(rename_all = "kebab-case")]
#[value(rename_all = "kebab-case")]
pub enum MergePolicy {
LocalDirect,
ChangeOpen,
ChangeAuto,
ChangeDirect,
}
impl MergePolicy {
pub fn narrow(self, requested: MergePolicy) -> crate::Result<MergePolicy> {
crate::policy::narrow_publication(self, requested)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Approvals {
Required,
None,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Gate {
Kind {
kind: GateKind,
},
Command {
command: Vec<String>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum GateKind {
Checks,
PrePush,
}