Преглед на файлове

Merge pull request #962 from kellda/close-command

Implement close command
Mark Rousskov преди 4 години
родител
ревизия
136fc3c9b5
променени са 6 файла, в които са добавени 72 реда и са изтрити 0 реда
  1. 8 0
      parser/src/command.rs
  2. 15 0
      parser/src/command/close.rs
  3. 5 0
      src/config.rs
  4. 17 0
      src/github.rs
  5. 2 0
      src/handlers.rs
  6. 25 0
      src/handlers/close.rs

+ 8 - 0
parser/src/command.rs

@@ -3,6 +3,7 @@ use crate::error::Error;
 use crate::token::{Token, Tokenizer};
 
 pub mod assign;
+pub mod close;
 pub mod glacier;
 pub mod nominate;
 pub mod ping;
@@ -23,6 +24,7 @@ pub enum Command<'a> {
     Prioritize(Result<prioritize::PrioritizeCommand, Error<'a>>),
     Second(Result<second::SecondCommand, Error<'a>>),
     Glacier(Result<glacier::GlacierCommand, Error<'a>>),
+    Close(Result<close::CloseCommand, Error<'a>>),
 }
 
 #[derive(Debug)]
@@ -110,6 +112,11 @@ impl<'a> Input<'a> {
             Command::Glacier,
             &original_tokenizer,
         ));
+        success.extend(parse_single_command(
+            close::CloseCommand::parse,
+            Command::Close,
+            &original_tokenizer,
+        ));
 
         if success.len() > 1 {
             panic!(
@@ -164,6 +171,7 @@ impl<'a> Command<'a> {
             Command::Prioritize(r) => r.is_ok(),
             Command::Second(r) => r.is_ok(),
             Command::Glacier(r) => r.is_ok(),
+            Command::Close(r) => r.is_ok(),
         }
     }
 

+ 15 - 0
parser/src/command/close.rs

@@ -0,0 +1,15 @@
+use crate::error::Error;
+use crate::token::{Token, Tokenizer};
+
+#[derive(PartialEq, Eq, Debug)]
+pub struct CloseCommand;
+
+impl CloseCommand {
+    pub fn parse<'a>(input: &mut Tokenizer<'a>) -> Result<Option<Self>, Error<'a>> {
+        if let Some(Token::Word("close")) = input.peek_token()? {
+            Ok(Some(Self))
+        } else {
+            Ok(None)
+        }
+    }
+}

+ 5 - 0
src/config.rs

@@ -24,6 +24,7 @@ pub(crate) struct Config {
     pub(crate) prioritize: Option<PrioritizeConfig>,
     pub(crate) major_change: Option<MajorChangeConfig>,
     pub(crate) glacier: Option<GlacierConfig>,
+    pub(crate) close: Option<CloseConfig>,
     pub(crate) autolabel: Option<AutolabelConfig>,
     pub(crate) notify_zulip: Option<NotifyZulipConfig>,
     pub(crate) github_releases: Option<GitHubReleasesConfig>,
@@ -138,6 +139,9 @@ pub(crate) struct MajorChangeConfig {
 #[derive(PartialEq, Eq, Debug, serde::Deserialize)]
 pub(crate) struct GlacierConfig {}
 
+#[derive(PartialEq, Eq, Debug, serde::Deserialize)]
+pub(crate) struct CloseConfig {}
+
 pub(crate) async fn get(gh: &GithubClient, repo: &str) -> Result<Arc<Config>, ConfigurationError> {
     if let Some(config) = get_cached_config(repo) {
         log::trace!("returning config for {} from cache", repo);
@@ -282,6 +286,7 @@ mod tests {
                 prioritize: None,
                 major_change: None,
                 glacier: None,
+                close: None,
                 autolabel: None,
                 notify_zulip: None,
                 github_releases: None,

+ 17 - 0
src/github.rs

@@ -593,6 +593,23 @@ impl Issue {
             .context("failed to set milestone")?;
         Ok(())
     }
+
+    pub async fn close(&self, client: &GithubClient) -> anyhow::Result<()> {
+        let edit_url = format!("{}/issues/{}", self.repository().url(), self.number);
+        #[derive(serde::Serialize)]
+        struct CloseIssue<'a> {
+            state: &'a str,
+        }
+        client
+            ._send_req(
+                client
+                    .patch(&edit_url)
+                    .json(&CloseIssue { state: "closed" }),
+            )
+            .await
+            .context("failed to close issue")?;
+        Ok(())
+    }
 }
 
 #[derive(serde::Serialize)]

+ 2 - 0
src/handlers.rs

@@ -25,6 +25,7 @@ impl fmt::Display for HandlerError {
 
 mod assign;
 mod autolabel;
+mod close;
 mod github_releases;
 mod glacier;
 mod major_change;
@@ -224,6 +225,7 @@ command_handlers! {
     prioritize: Prioritize,
     relabel: Relabel,
     major_change: Second,
+    close: Close,
 }
 
 pub struct Context {

+ 25 - 0
src/handlers/close.rs

@@ -0,0 +1,25 @@
+//! Allows to close an issue or a PR
+
+use crate::{config::CloseConfig, github::Event, handlers::Context, interactions::ErrorComment};
+use parser::command::close::CloseCommand;
+
+pub(super) async fn handle_command(
+    ctx: &Context,
+    _config: &CloseConfig,
+    event: &Event,
+    _cmd: CloseCommand,
+) -> anyhow::Result<()> {
+    let issue = event.issue().unwrap();
+    let is_team_member = event
+        .user()
+        .is_team_member(&ctx.github)
+        .await
+        .unwrap_or(false);
+    if !is_team_member {
+        let cmnt = ErrorComment::new(&issue, "Only team members can close issues.");
+        cmnt.post(&ctx.github).await?;
+        return Ok(());
+    }
+    issue.close(&ctx.github).await?;
+    Ok(())
+}