@@ -5,6 +5,7 @@ use chrono::{DateTime, FixedOffset, Utc};
5
5
use futures:: { future:: BoxFuture , FutureExt } ;
6
6
use hyper:: header:: HeaderValue ;
7
7
use once_cell:: sync:: OnceCell ;
8
+ use regex:: Regex ;
8
9
use reqwest:: header:: { AUTHORIZATION , USER_AGENT } ;
9
10
use reqwest:: { Client , Request , RequestBuilder , Response , StatusCode } ;
10
11
use std:: collections:: { HashMap , HashSet } ;
@@ -509,19 +510,6 @@ impl Issue {
509
510
Ok ( comment)
510
511
}
511
512
512
- // returns an array of one element
513
- pub async fn get_first_comment ( & self , client : & GithubClient ) -> anyhow:: Result < Vec < Comment > > {
514
- let comment_url = format ! (
515
- "{}/issues/{}/comments?page=1&per_page=1" ,
516
- self . repository( ) . url( client) ,
517
- self . number,
518
- ) ;
519
- Ok ( client
520
- . json :: < Vec < Comment > > ( client. get ( & comment_url) )
521
- . await ?)
522
- }
523
-
524
- // returns an array of one element
525
513
pub async fn get_first100_comments (
526
514
& self ,
527
515
client : & GithubClient ,
@@ -1763,12 +1751,22 @@ impl<'q> IssuesQuery for Query<'q> {
1763
1751
} ;
1764
1752
1765
1753
let mcp_details = if include_mcp_details {
1766
- let first_comment = issue. get_first_comment ( & client) . await ?;
1767
- let split = re_zulip_link
1768
- . split ( & first_comment[ 0 ] . body )
1769
- . collect :: < Vec < & str > > ( ) ;
1770
- let zulip_link = split. last ( ) . unwrap_or ( & "#" ) . to_string ( ) ;
1771
- Some ( crate :: actions:: MCPDetails { zulip_link } )
1754
+ let first100_comments = issue. get_first100_comments ( & client) . await ?;
1755
+ let ( zulip_link, concerns) = if !first100_comments. is_empty ( ) {
1756
+ let split = re_zulip_link
1757
+ . split ( & first100_comments[ 0 ] . body )
1758
+ . collect :: < Vec < & str > > ( ) ;
1759
+ let zulip_link = split. last ( ) . unwrap_or ( & "#" ) . to_string ( ) ;
1760
+ let concerns = find_open_concerns ( first100_comments) ;
1761
+ ( zulip_link, concerns)
1762
+ } else {
1763
+ ( "" . to_string ( ) , None )
1764
+ } ;
1765
+
1766
+ Some ( crate :: actions:: MCPDetails {
1767
+ zulip_link,
1768
+ concerns,
1769
+ } )
1772
1770
} else {
1773
1771
None
1774
1772
} ;
@@ -1800,6 +1798,56 @@ impl<'q> IssuesQuery for Query<'q> {
1800
1798
}
1801
1799
}
1802
1800
1801
+ /// Return open concerns filed in an issue under MCP/RFC process
1802
+ /// Concerns are marked by `@rfcbot concern` and `@rfcbot resolve`
1803
+ fn find_open_concerns ( comments : Vec < Comment > ) -> Option < Vec < ( String , String ) > > {
1804
+ let re_concern_raise =
1805
+ Regex :: new ( r"@rfcbot concern (?P<concern_title>.*)" ) . expect ( "Invalid regexp" ) ;
1806
+ let re_concern_solve =
1807
+ Regex :: new ( r"@rfcbot resolve (?P<concern_title>.*)" ) . expect ( "Invalid regexp" ) ;
1808
+ let mut raised: HashMap < String , String > = HashMap :: new ( ) ;
1809
+ let mut solved: HashMap < String , String > = HashMap :: new ( ) ;
1810
+
1811
+ for comment in comments {
1812
+ // Parse the comment and look for text markers to raise or resolve concerns
1813
+ let comment_lines = comment. body . lines ( ) ;
1814
+ for line in comment_lines {
1815
+ let r: Vec < & str > = re_concern_raise
1816
+ . captures_iter ( line)
1817
+ . map ( |caps| caps. name ( "concern_title" ) . map ( |f| f. as_str ( ) ) . unwrap_or ( "" ) )
1818
+ . collect ( ) ;
1819
+ let s: Vec < & str > = re_concern_solve
1820
+ . captures_iter ( line)
1821
+ . map ( |caps| caps. name ( "concern_title" ) . map ( |f| f. as_str ( ) ) . unwrap_or ( "" ) )
1822
+ . collect ( ) ;
1823
+
1824
+ // pick the first match only
1825
+ if !r. is_empty ( ) {
1826
+ let x = r[ 0 ] . replace ( "@rfcbot concern" , "" ) ;
1827
+ raised. insert ( x. trim ( ) . to_string ( ) , comment. html_url . to_string ( ) ) ;
1828
+ }
1829
+ if !s. is_empty ( ) {
1830
+ let x = s[ 0 ] . replace ( "@rfcbot resolve" , "" ) ;
1831
+ solved. insert ( x. trim ( ) . to_string ( ) , comment. html_url . to_string ( ) ) ;
1832
+ }
1833
+ }
1834
+ }
1835
+
1836
+ // remove solved concerns and return the rest
1837
+ let unresolved_concerns = raised
1838
+ . iter ( )
1839
+ . filter_map ( |( & ref title, & ref comment_url) | {
1840
+ if !solved. contains_key ( title) {
1841
+ Some ( ( title. to_string ( ) , comment_url. to_string ( ) ) )
1842
+ } else {
1843
+ None
1844
+ }
1845
+ } )
1846
+ . collect ( ) ;
1847
+
1848
+ Some ( unresolved_concerns)
1849
+ }
1850
+
1803
1851
#[ derive( Debug , serde:: Deserialize ) ]
1804
1852
#[ serde( rename_all = "snake_case" ) ]
1805
1853
pub enum CreateKind {
0 commit comments