@@ -4,18 +4,22 @@ use async_trait::async_trait;
4
4
use bytes:: Bytes ;
5
5
use chrono:: { DateTime , FixedOffset , Utc } ;
6
6
use futures:: { FutureExt , future:: BoxFuture } ;
7
+ use itertools:: Itertools ;
7
8
use octocrab:: models:: { Author , AuthorAssociation } ;
8
9
use regex:: Regex ;
9
10
use reqwest:: header:: { AUTHORIZATION , USER_AGENT } ;
10
11
use reqwest:: { Client , Request , RequestBuilder , Response , StatusCode } ;
11
12
use std:: collections:: { HashMap , HashSet } ;
12
- use std:: sync:: OnceLock ;
13
+ use std:: sync:: { LazyLock , OnceLock } ;
13
14
use std:: {
14
15
fmt,
15
16
time:: { Duration , SystemTime } ,
16
17
} ;
17
18
use tracing as log;
18
19
20
+ static EMOJI_REGEX : LazyLock < Regex > =
21
+ LazyLock :: new ( || Regex :: new ( r"[\p{Emoji}\p{Emoji_Presentation}]" ) . unwrap ( ) ) ;
22
+
19
23
pub type UserId = u64 ;
20
24
pub type PullRequestNumber = u64 ;
21
25
@@ -601,11 +605,7 @@ impl fmt::Display for AmbiguousLabelMatch {
601
605
f,
602
606
"Unsure which label to use for `{}` - could be one of: {}" ,
603
607
self . requested_label,
604
- self . labels
605
- . iter( )
606
- . map( |l| format!( "`{}`" , l) )
607
- . collect:: <Vec <_>>( )
608
- . join( ", " )
608
+ self . labels. iter( ) . map( |l| format!( "`{}`" , l) ) . join( ", " )
609
609
)
610
610
}
611
611
}
@@ -748,20 +748,23 @@ impl Issue {
748
748
async fn normalize_and_match_labels (
749
749
& self ,
750
750
client : & GithubClient ,
751
- requested_labels : & [ String ] ,
751
+ requested_labels : & [ & str ] ,
752
752
) -> anyhow:: Result < Vec < String > > {
753
- let available_labels = self . repository ( ) . labels ( client) . await . unwrap_or_default ( ) ;
753
+ let available_labels = self
754
+ . repository ( )
755
+ . labels ( client)
756
+ . await
757
+ . context ( "unable to retrieve the repository labels" ) ?;
754
758
755
- let emoji_regex: Regex = Regex :: new ( r"[\p{Emoji}\p{Emoji_Presentation}]" ) . unwrap ( ) ;
756
- let normalize = |s : & str | emoji_regex. replace_all ( s, "" ) . trim ( ) . to_lowercase ( ) ;
759
+ let normalize = |s : & str | EMOJI_REGEX . replace_all ( s, "" ) . trim ( ) . to_lowercase ( ) ;
757
760
758
761
let mut found_labels = Vec :: with_capacity ( requested_labels. len ( ) ) ;
759
762
let mut unknown_labels = Vec :: new ( ) ;
760
763
761
764
for requested_label in requested_labels {
762
765
// First look for an exact match
763
766
if let Some ( found) = available_labels. iter ( ) . find ( |l| l. name == * requested_label) {
764
- found_labels. push ( & found. name ) ;
767
+ found_labels. push ( found. name . clone ( ) ) ;
765
768
continue ;
766
769
}
767
770
@@ -774,36 +777,40 @@ impl Issue {
774
777
. filter ( |l| normalize ( & l. name ) == normalized_requested)
775
778
. collect :: < Vec < _ > > ( ) ;
776
779
777
- if found. is_empty ( ) {
778
- unknown_labels. push ( requested_label. as_str ( ) ) ;
779
- } else if found. len ( ) > 1 {
780
- return Err ( AmbiguousLabelMatch {
781
- requested_label : requested_label. clone ( ) ,
782
- labels : found. into_iter ( ) . map ( |l| l. name . clone ( ) ) . collect ( ) ,
780
+ match found[ ..] {
781
+ [ ] => {
782
+ unknown_labels. push ( requested_label) ;
783
783
}
784
- . into ( ) ) ;
785
- } else {
786
- found_labels. push ( & found. first ( ) . unwrap ( ) . name ) ;
787
- }
784
+ [ label] => {
785
+ found_labels. push ( label. name . clone ( ) ) ;
786
+ }
787
+ [ ..] => {
788
+ return Err ( AmbiguousLabelMatch {
789
+ requested_label : requested_label. to_string ( ) ,
790
+ labels : found. into_iter ( ) . map ( |l| l. name . clone ( ) ) . collect ( ) ,
791
+ }
792
+ . into ( ) ) ;
793
+ }
794
+ } ;
788
795
}
789
796
790
797
if !unknown_labels. is_empty ( ) {
791
798
return Err ( UnknownLabels {
792
- labels : unknown_labels. into_iter ( ) . map ( String :: from ) . collect ( ) ,
799
+ labels : unknown_labels. into_iter ( ) . map ( |s| s . to_string ( ) ) . collect ( ) ,
793
800
}
794
801
. into ( ) ) ;
795
802
}
796
803
797
- Ok ( found_labels. into_iter ( ) . map ( |s| s . clone ( ) ) . collect ( ) )
804
+ Ok ( found_labels)
798
805
}
799
806
800
807
pub async fn remove_label ( & self , client : & GithubClient , label : & str ) -> anyhow:: Result < ( ) > {
801
808
log:: info!( "remove_label from {}: {:?}" , self . global_id( ) , label) ;
802
809
803
- let normalized_labels = self
804
- . normalize_and_match_labels ( client , & [ label . to_string ( ) ] )
805
- . await ? ;
806
- let label = normalized_labels . first ( ) . unwrap ( ) ;
810
+ let normalized_labels = self . normalize_and_match_labels ( client , & [ label ] ) . await ? ;
811
+ let label = normalized_labels
812
+ . first ( )
813
+ . context ( "failed to find label on repository" ) ? ;
807
814
log:: info!(
808
815
"remove_label from {}: matched label to {:?}" ,
809
816
self . global_id( ) ,
@@ -849,7 +856,7 @@ impl Issue {
849
856
let labels = self
850
857
. normalize_and_match_labels (
851
858
client,
852
- & labels. into_iter ( ) . map ( |l| l. name ) . collect :: < Vec < _ > > ( ) ,
859
+ & labels. iter ( ) . map ( |l| l. name . as_str ( ) ) . collect :: < Vec < _ > > ( ) ,
853
860
)
854
861
. await ?;
855
862
log:: info!(
0 commit comments