File tree 5 files changed +108
-0
lines changed
5 files changed +108
-0
lines changed Original file line number Diff line number Diff line change @@ -13,6 +13,7 @@ pub mod prioritize;
13
13
pub mod relabel;
14
14
pub mod second;
15
15
pub mod shortcut;
16
+ pub mod transfer;
16
17
17
18
#[ derive( Debug , PartialEq ) ]
18
19
pub enum Command < ' a > {
@@ -26,6 +27,7 @@ pub enum Command<'a> {
26
27
Shortcut ( Result < shortcut:: ShortcutCommand , Error < ' a > > ) ,
27
28
Close ( Result < close:: CloseCommand , Error < ' a > > ) ,
28
29
Note ( Result < note:: NoteCommand , Error < ' a > > ) ,
30
+ Transfer ( Result < transfer:: TransferCommand , Error < ' a > > ) ,
29
31
}
30
32
31
33
#[ derive( Debug ) ]
@@ -132,6 +134,11 @@ impl<'a> Input<'a> {
132
134
Command :: Close ,
133
135
& original_tokenizer,
134
136
) ) ;
137
+ success. extend ( parse_single_command (
138
+ transfer:: TransferCommand :: parse,
139
+ Command :: Transfer ,
140
+ & original_tokenizer,
141
+ ) ) ;
135
142
136
143
if success. len ( ) > 1 {
137
144
panic ! (
@@ -207,6 +214,7 @@ impl<'a> Command<'a> {
207
214
Command :: Shortcut ( r) => r. is_ok ( ) ,
208
215
Command :: Close ( r) => r. is_ok ( ) ,
209
216
Command :: Note ( r) => r. is_ok ( ) ,
217
+ Command :: Transfer ( r) => r. is_ok ( ) ,
210
218
}
211
219
}
212
220
Original file line number Diff line number Diff line change
1
+ //! Parses the `@bot transfer reponame` command.
2
+
3
+ use crate :: error:: Error ;
4
+ use crate :: token:: { Token , Tokenizer } ;
5
+ use std:: fmt;
6
+
7
+ #[ derive( Debug , PartialEq , Eq ) ]
8
+ pub struct TransferCommand ( pub String ) ;
9
+
10
+ #[ derive( Debug ) ]
11
+ pub enum ParseError {
12
+ MissingRepo ,
13
+ }
14
+
15
+ impl std:: error:: Error for ParseError { }
16
+
17
+ impl fmt:: Display for ParseError {
18
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
19
+ match self {
20
+ ParseError :: MissingRepo => write ! ( f, "missing repository name" ) ,
21
+ }
22
+ }
23
+ }
24
+
25
+ impl TransferCommand {
26
+ pub fn parse < ' a > ( input : & mut Tokenizer < ' a > ) -> Result < Option < Self > , Error < ' a > > {
27
+ if !matches ! ( input. peek_token( ) ?, Some ( Token :: Word ( "transfer" ) ) ) {
28
+ return Ok ( None ) ;
29
+ }
30
+ input. next_token ( ) ?;
31
+ let repo = if let Some ( Token :: Word ( repo) ) = input. next_token ( ) ? {
32
+ repo. to_owned ( )
33
+ } else {
34
+ return Err ( input. error ( ParseError :: MissingRepo ) ) ;
35
+ } ;
36
+ Ok ( Some ( TransferCommand ( repo) ) )
37
+ }
38
+ }
Original file line number Diff line number Diff line change @@ -39,6 +39,7 @@ pub(crate) struct Config {
39
39
// We want this validation to run even without the entry in the config file
40
40
#[ serde( default = "ValidateConfig::default" ) ]
41
41
pub ( crate ) validate_config : Option < ValidateConfig > ,
42
+ pub ( crate ) transfer : Option < TransferConfig > ,
42
43
}
43
44
44
45
#[ derive( PartialEq , Eq , Debug , serde:: Deserialize ) ]
@@ -317,6 +318,11 @@ pub(crate) struct GitHubReleasesConfig {
317
318
pub ( crate ) changelog_branch : String ,
318
319
}
319
320
321
+ #[ derive( PartialEq , Eq , Debug , serde:: Deserialize ) ]
322
+ #[ serde( rename_all = "kebab-case" ) ]
323
+ #[ serde( deny_unknown_fields) ]
324
+ pub ( crate ) struct TransferConfig { }
325
+
320
326
fn get_cached_config ( repo : & str ) -> Option < Result < Arc < Config > , ConfigurationError > > {
321
327
let cache = CONFIG_CACHE . read ( ) . unwrap ( ) ;
322
328
cache. get ( repo) . and_then ( |( config, fetch_time) | {
@@ -463,6 +469,7 @@ mod tests {
463
469
mentions: None ,
464
470
no_merges: None ,
465
471
validate_config: Some ( ValidateConfig { } ) ,
472
+ transfer: None ,
466
473
}
467
474
) ;
468
475
}
Original file line number Diff line number Diff line change @@ -46,6 +46,7 @@ mod review_submitted;
46
46
mod rfc_helper;
47
47
pub mod rustc_commits;
48
48
mod shortcut;
49
+ mod transfer;
49
50
pub mod types_planning_updates;
50
51
mod validate_config;
51
52
@@ -290,6 +291,7 @@ command_handlers! {
290
291
shortcut: Shortcut ,
291
292
close: Close ,
292
293
note: Note ,
294
+ transfer: Transfer ,
293
295
}
294
296
295
297
pub struct Context {
Original file line number Diff line number Diff line change
1
+ //! Handles the `@rustbot transfer reponame` command to transfer an issue to
2
+ //! another repository.
3
+
4
+ use crate :: { config:: TransferConfig , github:: Event , handlers:: Context } ;
5
+ use parser:: command:: transfer:: TransferCommand ;
6
+
7
+ pub ( super ) async fn handle_command (
8
+ ctx : & Context ,
9
+ _config : & TransferConfig ,
10
+ event : & Event ,
11
+ input : TransferCommand ,
12
+ ) -> anyhow:: Result < ( ) > {
13
+ let issue = event. issue ( ) . unwrap ( ) ;
14
+ if issue. is_pr ( ) {
15
+ issue
16
+ . post_comment ( & ctx. github , "Only issues can be transferred." )
17
+ . await ?;
18
+ return Ok ( ( ) ) ;
19
+ }
20
+ if !event
21
+ . user ( )
22
+ . is_team_member ( & ctx. github )
23
+ . await
24
+ . ok ( )
25
+ . unwrap_or ( false )
26
+ {
27
+ issue
28
+ . post_comment (
29
+ & ctx. github ,
30
+ "Only team members may use the `transfer` command." ,
31
+ )
32
+ . await ?;
33
+ return Ok ( ( ) ) ;
34
+ }
35
+
36
+ let repo = input. 0 ;
37
+ let repo = repo. strip_prefix ( "rust-lang/" ) . unwrap_or ( & repo) ;
38
+ if repo. contains ( '/' ) {
39
+ issue
40
+ . post_comment ( & ctx. github , "Cross-organization transfers are not allowed." )
41
+ . await ?;
42
+ return Ok ( ( ) ) ;
43
+ }
44
+
45
+ if let Err ( e) = issue. transfer ( & ctx. github , "rust-lang" , & repo) . await {
46
+ issue
47
+ . post_comment ( & ctx. github , & format ! ( "Failed to transfer issue:\n {e:?}" ) )
48
+ . await ?;
49
+ return Ok ( ( ) ) ;
50
+ }
51
+
52
+ Ok ( ( ) )
53
+ }
You can’t perform that action at this time.
0 commit comments