@@ -16,8 +16,6 @@ use std::{
16
16
} ;
17
17
use tracing as log;
18
18
19
- pub mod labels;
20
-
21
19
pub type UserId = u64 ;
22
20
pub type PullRequestNumber = u64 ;
23
21
@@ -567,15 +565,38 @@ impl IssueRepository {
567
565
format ! ( "{}/{}" , self . organization, self . repository)
568
566
}
569
567
570
- async fn labels ( & self , client : & GithubClient ) -> anyhow:: Result < Vec < Label > > {
571
- let url = format ! ( "{}/labels" , self . url( client) ) ;
572
- client
573
- . json ( client. get ( & url) )
574
- . await
575
- . context ( "failed to get labels" )
568
+ async fn has_label ( & self , client : & GithubClient , label : & str ) -> anyhow:: Result < bool > {
569
+ #[ allow( clippy:: redundant_pattern_matching) ]
570
+ let url = format ! ( "{}/labels/{}" , self . url( client) , label) ;
571
+ match client. send_req ( client. get ( & url) ) . await {
572
+ Ok ( _) => Ok ( true ) ,
573
+ Err ( e) => {
574
+ if e. downcast_ref :: < reqwest:: Error > ( )
575
+ . map_or ( false , |e| e. status ( ) == Some ( StatusCode :: NOT_FOUND ) )
576
+ {
577
+ Ok ( false )
578
+ } else {
579
+ Err ( e)
580
+ }
581
+ }
582
+ }
576
583
}
577
584
}
578
585
586
+ #[ derive( Debug ) ]
587
+ pub ( crate ) struct UnknownLabels {
588
+ labels : Vec < String > ,
589
+ }
590
+
591
+ // NOTE: This is used to post the Github comment; make sure it's valid markdown.
592
+ impl fmt:: Display for UnknownLabels {
593
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
594
+ write ! ( f, "Unknown labels: {}" , & self . labels. join( ", " ) )
595
+ }
596
+ }
597
+
598
+ impl std:: error:: Error for UnknownLabels { }
599
+
579
600
impl Issue {
580
601
pub fn to_zulip_github_reference ( & self ) -> ZulipGitHubReference {
581
602
ZulipGitHubReference {
@@ -709,39 +730,8 @@ impl Issue {
709
730
Ok ( ( ) )
710
731
}
711
732
712
- async fn normalize_and_match_labels (
713
- & self ,
714
- client : & GithubClient ,
715
- requested_labels : & [ & str ] ,
716
- ) -> anyhow:: Result < Vec < String > > {
717
- let available_labels = self
718
- . repository ( )
719
- . labels ( client)
720
- . await
721
- . context ( "unable to retrieve the repository labels" ) ?;
722
-
723
- labels:: normalize_and_match_labels (
724
- & available_labels
725
- . iter ( )
726
- . map ( |l| l. name . as_str ( ) )
727
- . collect :: < Vec < _ > > ( ) ,
728
- requested_labels,
729
- )
730
- }
731
-
732
733
pub async fn remove_label ( & self , client : & GithubClient , label : & str ) -> anyhow:: Result < ( ) > {
733
734
log:: info!( "remove_label from {}: {:?}" , self . global_id( ) , label) ;
734
-
735
- let normalized_labels = self . normalize_and_match_labels ( client, & [ label] ) . await ?;
736
- let label = normalized_labels
737
- . first ( )
738
- . context ( "failed to find label on repository" ) ?;
739
- log:: info!(
740
- "remove_label from {}: matched label to {:?}" ,
741
- self . global_id( ) ,
742
- label
743
- ) ;
744
-
745
735
// DELETE /repos/:owner/:repo/issues/:number/labels/{name}
746
736
let url = format ! (
747
737
"{repo_url}/issues/{number}/labels/{name}" ,
@@ -777,19 +767,6 @@ impl Issue {
777
767
labels : Vec < Label > ,
778
768
) -> anyhow:: Result < ( ) > {
779
769
log:: info!( "add_labels: {} +{:?}" , self . global_id( ) , labels) ;
780
-
781
- let labels = self
782
- . normalize_and_match_labels (
783
- client,
784
- & labels. iter ( ) . map ( |l| l. name . as_str ( ) ) . collect :: < Vec < _ > > ( ) ,
785
- )
786
- . await ?;
787
- log:: info!(
788
- "add_labels: {} matched requested labels to +{:?}" ,
789
- self . global_id( ) ,
790
- labels
791
- ) ;
792
-
793
770
// POST /repos/:owner/:repo/issues/:number/labels
794
771
// repo_url = https://api.github.com/repos/Codertocat/Hello-World
795
772
let url = format ! (
@@ -801,7 +778,8 @@ impl Issue {
801
778
// Don't try to add labels already present on this issue.
802
779
let labels = labels
803
780
. into_iter ( )
804
- . filter ( |l| !self . labels ( ) . iter ( ) . any ( |existing| existing. name == * l) )
781
+ . filter ( |l| !self . labels ( ) . contains ( & l) )
782
+ . map ( |l| l. name )
805
783
. collect :: < Vec < _ > > ( ) ;
806
784
807
785
log:: info!( "add_labels: {} filtered to {:?}" , self . global_id( ) , labels) ;
@@ -810,13 +788,32 @@ impl Issue {
810
788
return Ok ( ( ) ) ;
811
789
}
812
790
791
+ let mut unknown_labels = vec ! [ ] ;
792
+ let mut known_labels = vec ! [ ] ;
793
+ for label in labels {
794
+ if !self . repository ( ) . has_label ( client, & label) . await ? {
795
+ unknown_labels. push ( label) ;
796
+ } else {
797
+ known_labels. push ( label) ;
798
+ }
799
+ }
800
+
801
+ if !unknown_labels. is_empty ( ) {
802
+ return Err ( UnknownLabels {
803
+ labels : unknown_labels,
804
+ }
805
+ . into ( ) ) ;
806
+ }
807
+
813
808
#[ derive( serde:: Serialize ) ]
814
809
struct LabelsReq {
815
810
labels : Vec < String > ,
816
811
}
817
812
818
813
client
819
- . send_req ( client. post ( & url) . json ( & LabelsReq { labels } ) )
814
+ . send_req ( client. post ( & url) . json ( & LabelsReq {
815
+ labels : known_labels,
816
+ } ) )
820
817
. await
821
818
. context ( "failed to add labels" ) ?;
822
819
@@ -3220,3 +3217,16 @@ impl Submodule {
3220
3217
client. repository ( fullname) . await
3221
3218
}
3222
3219
}
3220
+
3221
+ #[ cfg( test) ]
3222
+ mod tests {
3223
+ use super :: * ;
3224
+
3225
+ #[ test]
3226
+ fn display_labels ( ) {
3227
+ let x = UnknownLabels {
3228
+ labels : vec ! [ "A-bootstrap" . into( ) , "xxx" . into( ) ] ,
3229
+ } ;
3230
+ assert_eq ! ( x. to_string( ) , "Unknown labels: A-bootstrap, xxx" ) ;
3231
+ }
3232
+ }
0 commit comments