Skip to main content

fallow_output/
pr_comment_post_plan.rs

1//! Pure posting policy for sticky PR comments.
2
3use crate::PrCommentEnvelope;
4
5#[derive(Clone, Debug, PartialEq, Eq)]
6pub struct ExistingPrComment {
7    pub id: String,
8    pub body: String,
9}
10
11pub struct PrCommentPostPlanInput<'a> {
12    pub envelope: &'a PrCommentEnvelope,
13    pub existing: Option<&'a ExistingPrComment>,
14}
15
16#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
17#[serde(rename_all = "snake_case")]
18pub enum PrCommentPostAction {
19    Create,
20    Update,
21    Skip,
22}
23
24#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
25#[serde(rename_all = "snake_case")]
26pub enum PrCommentPostSkipReason {
27    CleanNoExistingComment,
28    Unchanged,
29}
30
31#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
32pub struct PrCommentPostPlan {
33    pub action: PrCommentPostAction,
34    pub marker_id: String,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub comment_id: Option<String>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub skip_reason: Option<PrCommentPostSkipReason>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub body: Option<String>,
41}
42
43#[must_use]
44pub fn plan_pr_comment_post(input: &PrCommentPostPlanInput<'_>) -> PrCommentPostPlan {
45    match input.existing {
46        Some(existing) if existing.body == input.envelope.body => skip_unchanged(input.envelope),
47        Some(existing) => update_existing(input.envelope, existing),
48        None if input.envelope.is_clean => skip_clean(input.envelope),
49        None => create_comment(input.envelope),
50    }
51}
52
53fn create_comment(envelope: &PrCommentEnvelope) -> PrCommentPostPlan {
54    PrCommentPostPlan {
55        action: PrCommentPostAction::Create,
56        marker_id: envelope.marker_id.clone(),
57        comment_id: None,
58        skip_reason: None,
59        body: Some(envelope.body.clone()),
60    }
61}
62
63fn update_existing(
64    envelope: &PrCommentEnvelope,
65    existing: &ExistingPrComment,
66) -> PrCommentPostPlan {
67    PrCommentPostPlan {
68        action: PrCommentPostAction::Update,
69        marker_id: envelope.marker_id.clone(),
70        comment_id: Some(existing.id.clone()),
71        skip_reason: None,
72        body: Some(envelope.body.clone()),
73    }
74}
75
76fn skip_clean(envelope: &PrCommentEnvelope) -> PrCommentPostPlan {
77    PrCommentPostPlan {
78        action: PrCommentPostAction::Skip,
79        marker_id: envelope.marker_id.clone(),
80        comment_id: None,
81        skip_reason: Some(PrCommentPostSkipReason::CleanNoExistingComment),
82        body: None,
83    }
84}
85
86fn skip_unchanged(envelope: &PrCommentEnvelope) -> PrCommentPostPlan {
87    PrCommentPostPlan {
88        action: PrCommentPostAction::Skip,
89        marker_id: envelope.marker_id.clone(),
90        comment_id: None,
91        skip_reason: Some(PrCommentPostSkipReason::Unchanged),
92        body: None,
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    fn envelope(is_clean: bool, body: &str) -> PrCommentEnvelope {
101        PrCommentEnvelope {
102            marker_id: "fallow-results".to_owned(),
103            body: body.to_owned(),
104            is_clean,
105            details_url: None,
106            check_summary: None,
107            truncation: crate::PrCommentTruncation::default(),
108        }
109    }
110
111    #[test]
112    fn clean_without_existing_comment_skips_create() {
113        let plan = plan_pr_comment_post(&PrCommentPostPlanInput {
114            envelope: &envelope(true, "clean"),
115            existing: None,
116        });
117
118        assert_eq!(plan.action, PrCommentPostAction::Skip);
119        assert_eq!(
120            plan.skip_reason,
121            Some(PrCommentPostSkipReason::CleanNoExistingComment)
122        );
123        assert_eq!(plan.body, None);
124    }
125
126    #[test]
127    fn clean_with_existing_comment_updates_existing_body() {
128        let current = envelope(true, "clean");
129        let existing = ExistingPrComment {
130            id: "42".to_owned(),
131            body: "old".to_owned(),
132        };
133
134        let plan = plan_pr_comment_post(&PrCommentPostPlanInput {
135            envelope: &current,
136            existing: Some(&existing),
137        });
138
139        assert_eq!(plan.action, PrCommentPostAction::Update);
140        assert_eq!(plan.comment_id.as_deref(), Some("42"));
141        assert_eq!(plan.body.as_deref(), Some("clean"));
142    }
143
144    #[test]
145    fn dirty_without_existing_comment_creates_comment() {
146        let current = envelope(false, "dirty");
147
148        let plan = plan_pr_comment_post(&PrCommentPostPlanInput {
149            envelope: &current,
150            existing: None,
151        });
152
153        assert_eq!(plan.action, PrCommentPostAction::Create);
154        assert_eq!(plan.body.as_deref(), Some("dirty"));
155    }
156
157    #[test]
158    fn identical_existing_comment_skips_update() {
159        let current = envelope(false, "same");
160        let existing = ExistingPrComment {
161            id: "42".to_owned(),
162            body: "same".to_owned(),
163        };
164
165        let plan = plan_pr_comment_post(&PrCommentPostPlanInput {
166            envelope: &current,
167            existing: Some(&existing),
168        });
169
170        assert_eq!(plan.action, PrCommentPostAction::Skip);
171        assert_eq!(plan.skip_reason, Some(PrCommentPostSkipReason::Unchanged));
172    }
173}