fallow_output/
pr_decision.rs1use serde::{Deserialize, Serialize};
2
3pub const PR_DECISION_SCHEMA: &str = "fallow-pr-decision/v1";
5
6#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
9pub struct PrDecisionSurface {
10 pub schema: String,
12 pub title: String,
14 pub conclusion: PrDecisionConclusion,
16 pub gates: Vec<PrDecisionGate>,
18 pub annotations: Vec<PrDecisionAnnotation>,
20 pub details: PrDecisionDetails,
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
27#[serde(rename_all = "snake_case")]
28pub enum PrDecisionConclusion {
29 Success,
31 Failure,
33 Neutral,
35 Skipped,
37}
38
39#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
41pub struct PrDecisionGate {
42 pub id: String,
44 pub label: String,
46 pub status: PrDecisionConclusion,
48 pub observed: String,
50 pub threshold: Option<String>,
52 pub scope: String,
54}
55
56#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
58pub struct PrDecisionAnnotation {
59 pub path: String,
61 pub line: u32,
63 pub level: PrDecisionAnnotationLevel,
65 pub title: String,
67 pub message: String,
69 pub raw_details: Option<String>,
71}
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
76#[serde(rename_all = "snake_case")]
77pub enum PrDecisionAnnotationLevel {
78 Notice,
80 Warning,
82 Failure,
84}
85
86#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
88pub struct PrDecisionDetails {
89 pub summary_markdown: String,
91 pub full_report_path: Option<String>,
93 pub details_url: Option<String>,
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn decision_surface_serializes_stable_schema() {
103 let surface = PrDecisionSurface {
104 schema: PR_DECISION_SCHEMA.to_owned(),
105 title: "Fallow".to_owned(),
106 conclusion: PrDecisionConclusion::Failure,
107 gates: vec![PrDecisionGate {
108 id: "duplication".to_owned(),
109 label: "Duplication".to_owned(),
110 status: PrDecisionConclusion::Failure,
111 observed: "9.1% on changed code".to_owned(),
112 threshold: Some("<= 3%".to_owned()),
113 scope: "new code".to_owned(),
114 }],
115 annotations: vec![PrDecisionAnnotation {
116 path: "src/app.ts".to_owned(),
117 line: 42,
118 level: PrDecisionAnnotationLevel::Warning,
119 title: "Duplication".to_owned(),
120 message: "Clone group found".to_owned(),
121 raw_details: Some("fallow/code-duplication".to_owned()),
122 }],
123 details: PrDecisionDetails {
124 summary_markdown: "Quality gate failed".to_owned(),
125 full_report_path: None,
126 details_url: None,
127 },
128 };
129
130 let json = serde_json::to_value(surface).expect("serializes");
131 assert_eq!(json["schema"], PR_DECISION_SCHEMA);
132 assert_eq!(json["conclusion"], "failure");
133 assert_eq!(json["annotations"][0]["level"], "warning");
134 }
135}