Skip to content

Commit 18dabcf

Browse files
committed
Make relabel deltas left to right consistent
1 parent b0a31bc commit 18dabcf

File tree

3 files changed

+97
-20
lines changed

3 files changed

+97
-20
lines changed

parser/src/command/relabel.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub enum LabelDelta {
1919
}
2020

2121
#[derive(Debug, PartialEq, Eq, Clone)]
22-
pub struct Label(String);
22+
pub struct Label(pub String);
2323

2424
#[derive(PartialEq, Eq, Debug)]
2525
pub enum ParseError {

src/github.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ impl User {
324324
}
325325
}
326326

327-
#[derive(PartialEq, Eq, Debug, Clone, serde::Deserialize)]
327+
#[derive(PartialEq, Eq, Debug, Clone, Ord, PartialOrd, serde::Deserialize)]
328328
pub struct Label {
329329
pub name: String,
330330
}

src/handlers/relabel.rs

Lines changed: 95 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
//! If the command was successful, there will be no feedback beyond the label change to reduce
99
//! notification noise.
1010
11+
use std::collections::BTreeSet;
12+
13+
use crate::github::Label;
1114
use crate::team_data::TeamClient;
1215
use crate::{
1316
config::RelabelConfig,
@@ -24,8 +27,6 @@ pub(super) async fn handle_command(
2427
event: &Event,
2528
input: RelabelCommand,
2629
) -> anyhow::Result<()> {
27-
let mut results = vec![];
28-
let mut to_add = vec![];
2930
for delta in &input.0 {
3031
let name = delta.label().as_str();
3132
let err = match check_filter(name, config, is_member(&event.user(), &ctx.team).await) {
@@ -46,21 +47,10 @@ pub(super) async fn handle_command(
4647
cmnt.post(&ctx.github).await?;
4748
return Ok(());
4849
}
49-
match delta {
50-
LabelDelta::Add(label) => {
51-
to_add.push(github::Label {
52-
name: label.to_string(),
53-
});
54-
}
55-
LabelDelta::Remove(label) => {
56-
results.push((
57-
label,
58-
event.issue().unwrap().remove_label(&ctx.github, &label),
59-
));
60-
}
61-
}
6250
}
6351

52+
let (to_add, to_remove) = compute_label_deltas(&input.0);
53+
6454
if let Err(e) = event
6555
.issue()
6656
.unwrap()
@@ -84,8 +74,13 @@ pub(super) async fn handle_command(
8474
return Err(e);
8575
}
8676

87-
for (label, res) in results {
88-
if let Err(e) = res.await {
77+
for label in to_remove {
78+
if let Err(e) = event
79+
.issue()
80+
.unwrap()
81+
.remove_label(&ctx.github, &label.name)
82+
.await
83+
{
8984
tracing::error!(
9085
"failed to remove {:?} from issue {}: {:?}",
9186
label,
@@ -182,10 +177,41 @@ fn match_pattern(pattern: &str, label: &str) -> anyhow::Result<MatchPatternResul
182177
})
183178
}
184179

180+
fn compute_label_deltas(deltas: &[LabelDelta]) -> (Vec<Label>, Vec<Label>) {
181+
let mut add = BTreeSet::new();
182+
let mut remove = BTreeSet::new();
183+
184+
for delta in deltas {
185+
match delta {
186+
LabelDelta::Add(label) => {
187+
let label = Label {
188+
name: label.to_string(),
189+
};
190+
if !remove.remove(&label) {
191+
add.insert(label);
192+
}
193+
}
194+
LabelDelta::Remove(label) => {
195+
let label = Label {
196+
name: label.to_string(),
197+
};
198+
if !add.remove(&label) {
199+
remove.insert(label);
200+
}
201+
}
202+
}
203+
}
204+
205+
(add.into_iter().collect(), remove.into_iter().collect())
206+
}
207+
185208
#[cfg(test)]
186209
mod tests {
210+
use parser::command::relabel::{Label, LabelDelta};
211+
187212
use super::{
188-
CheckFilterResult, MatchPatternResult, TeamMembership, check_filter, match_pattern,
213+
CheckFilterResult, MatchPatternResult, TeamMembership, check_filter, compute_label_deltas,
214+
match_pattern,
189215
};
190216
use crate::config::RelabelConfig;
191217

@@ -252,4 +278,55 @@ mod tests {
252278
}
253279
Ok(())
254280
}
281+
282+
#[test]
283+
fn test_compute_label_deltas() {
284+
use crate::github::Label as GitHubLabel;
285+
286+
let mut deltas = vec![
287+
LabelDelta::Add(Label("I-nominated".to_string())),
288+
LabelDelta::Add(Label("I-nominated".to_string())),
289+
LabelDelta::Add(Label("I-lang-nominated".to_string())),
290+
LabelDelta::Add(Label("I-libs-nominated".to_string())),
291+
LabelDelta::Remove(Label("I-lang-nominated".to_string())),
292+
];
293+
294+
assert_eq!(
295+
compute_label_deltas(&deltas),
296+
(
297+
vec![
298+
GitHubLabel {
299+
name: "I-libs-nominated".to_string()
300+
},
301+
GitHubLabel {
302+
name: "I-nominated".to_string()
303+
},
304+
],
305+
vec![],
306+
),
307+
);
308+
309+
deltas.push(LabelDelta::Remove(Label("needs-triage".to_string())));
310+
deltas.push(LabelDelta::Add(Label("I-lang-nominated".to_string())));
311+
312+
assert_eq!(
313+
compute_label_deltas(&deltas),
314+
(
315+
vec![
316+
GitHubLabel {
317+
name: "I-lang-nominated".to_string()
318+
},
319+
GitHubLabel {
320+
name: "I-libs-nominated".to_string()
321+
},
322+
GitHubLabel {
323+
name: "I-nominated".to_string()
324+
},
325+
],
326+
vec![GitHubLabel {
327+
name: "needs-triage".to_string()
328+
}],
329+
),
330+
);
331+
}
255332
}

0 commit comments

Comments
 (0)