@@ -5,6 +5,7 @@ use chrono::{DateTime, FixedOffset, Utc};
55use futures:: { future:: BoxFuture , FutureExt } ;
66use hyper:: header:: HeaderValue ;
77use once_cell:: sync:: OnceCell ;
8+ use regex:: Regex ;
89use reqwest:: header:: { AUTHORIZATION , USER_AGENT } ;
910use reqwest:: { Client , Request , RequestBuilder , Response , StatusCode } ;
1011use std:: collections:: { HashMap , HashSet } ;
@@ -509,19 +510,6 @@ impl Issue {
509510 Ok ( comment)
510511 }
511512
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
525513 pub async fn get_first100_comments (
526514 & self ,
527515 client : & GithubClient ,
@@ -1763,12 +1751,22 @@ impl<'q> IssuesQuery for Query<'q> {
17631751 } ;
17641752
17651753 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+ } )
17721770 } else {
17731771 None
17741772 } ;
@@ -1800,6 +1798,56 @@ impl<'q> IssuesQuery for Query<'q> {
18001798 }
18011799}
18021800
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+
18031851#[ derive( Debug , serde:: Deserialize ) ]
18041852#[ serde( rename_all = "snake_case" ) ]
18051853pub enum CreateKind {
0 commit comments