Browse Source

add shorcut parser and config

Llandy Riveron Del Risco 3 years ago
parent
commit
00ce648db6
3 changed files with 116 additions and 0 deletions
  1. 8 0
      parser/src/command.rs
  2. 95 0
      parser/src/command/shortcut.rs
  3. 13 0
      src/config.rs

+ 8 - 0
parser/src/command.rs

@@ -10,6 +10,7 @@ pub mod ping;
 pub mod prioritize;
 pub mod relabel;
 pub mod second;
+pub mod shortcut;
 
 pub fn find_command_start(input: &str, bot: &str) -> Option<usize> {
     input.to_ascii_lowercase().find(&format!("@{}", bot))
@@ -24,6 +25,7 @@ pub enum Command<'a> {
     Prioritize(Result<prioritize::PrioritizeCommand, Error<'a>>),
     Second(Result<second::SecondCommand, Error<'a>>),
     Glacier(Result<glacier::GlacierCommand, Error<'a>>),
+    Shortcut(Result<shortcut::ShortcutCommand, Error<'a>>),
     Close(Result<close::CloseCommand, Error<'a>>),
 }
 
@@ -119,6 +121,11 @@ impl<'a> Input<'a> {
             Command::Glacier,
             &original_tokenizer,
         ));
+        success.extend(parse_single_command(
+            shortcut::ShortcutCommand::parse,
+            Command::Shortcut,
+            &original_tokenizer,
+        ));
         success.extend(parse_single_command(
             close::CloseCommand::parse,
             Command::Close,
@@ -182,6 +189,7 @@ impl<'a> Command<'a> {
             Command::Prioritize(r) => r.is_ok(),
             Command::Second(r) => r.is_ok(),
             Command::Glacier(r) => r.is_ok(),
+            Command::Shortcut(r) => r.is_ok(),
             Command::Close(r) => r.is_ok(),
         }
     }

+ 95 - 0
parser/src/command/shortcut.rs

@@ -0,0 +1,95 @@
+//! The shortcut command parser.
+//!
+//! This can parse predefined shortcut input, single word commands.
+//!
+//! The grammar is as follows:
+//!
+//! ```text
+//! Command: `@bot ready`, or `@bot author`.
+//! ```
+
+use crate::error::Error;
+use crate::token::{Token, Tokenizer};
+use std::fmt;
+
+#[derive(PartialEq, Eq, Debug)]
+pub enum ShortcutCommand {
+    Ready,
+    Author,
+}
+
+#[derive(PartialEq, Eq, Debug)]
+pub enum ParseError {
+    ExpectedEnd,
+}
+
+impl std::error::Error for ParseError {}
+
+impl fmt::Display for ParseError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            ParseError::ExpectedEnd => write!(f, "expected end of command"),
+        }
+    }
+}
+
+impl ShortcutCommand {
+    pub fn parse<'a>(input: &mut Tokenizer<'a>) -> Result<Option<Self>, Error<'a>> {
+        let mut toks = input.clone();
+        if let Some(Token::Word("ready")) = toks.peek_token()? {
+            toks.next_token()?;
+            if let Some(Token::Dot) | Some(Token::EndOfLine) = toks.peek_token()? {
+                toks.next_token()?;
+                *input = toks;
+                return Ok(Some(ShortcutCommand::Ready));
+            } else {
+                return Err(toks.error(ParseError::ExpectedEnd));
+            }
+        } else if let Some(Token::Word("author")) = toks.peek_token()? {
+            toks.next_token()?;
+            if let Some(Token::Dot) | Some(Token::EndOfLine) = toks.peek_token()? {
+                toks.next_token()?;
+                *input = toks;
+                return Ok(Some(ShortcutCommand::Author));
+            } else {
+                return Err(toks.error(ParseError::ExpectedEnd));
+            }
+        } else {
+            return Ok(None);
+        }
+    }
+}
+
+#[cfg(test)]
+fn parse(input: &str) -> Result<Option<ShortcutCommand>, Error<'_>> {
+    let mut toks = Tokenizer::new(input);
+    Ok(ShortcutCommand::parse(&mut toks)?)
+}
+
+#[test]
+fn test_1() {
+    assert_eq!(parse("ready."), Ok(Some(ShortcutCommand::Ready)),);
+}
+
+#[test]
+fn test_2() {
+    assert_eq!(parse("ready"), Ok(Some(ShortcutCommand::Ready)),);
+}
+
+#[test]
+fn test_3() {
+    assert_eq!(parse("author"), Ok(Some(ShortcutCommand::Author)),);
+}
+
+#[test]
+fn test_4() {
+    use std::error::Error;
+    assert_eq!(
+        parse("ready word")
+            .unwrap_err()
+            .source()
+            .unwrap()
+            .downcast_ref(),
+        Some(&ParseError::ExpectedEnd),
+    );
+}

+ 13 - 0
src/config.rs

@@ -29,6 +29,7 @@ pub(crate) struct Config {
     pub(crate) notify_zulip: Option<NotifyZulipConfig>,
     pub(crate) github_releases: Option<GitHubReleasesConfig>,
     pub(crate) review_submitted: Option<ReviewSubmittedConfig>,
+    pub(crate) shortcut: Option<ShortcutConfig>,
 }
 
 #[derive(PartialEq, Eq, Debug, serde::Deserialize)]
@@ -82,6 +83,12 @@ pub(crate) struct RelabelConfig {
     pub(crate) allow_unauthenticated: Vec<String>,
 }
 
+#[derive(PartialEq, Eq, Debug, serde::Deserialize)]
+pub(crate) struct ShortcutConfig {
+    #[serde(default)]
+    pub(crate) allow: Vec<String>,
+}
+
 #[derive(PartialEq, Eq, Debug, serde::Deserialize)]
 pub(crate) struct PrioritizeConfig {
     pub(crate) label: String,
@@ -255,6 +262,11 @@ mod tests {
             release = "T-release"
             core = "T-core"
             infra = "T-infra"
+
+            [shortcut]
+            allow = [
+                "ready"
+            ]
         "#;
         let config = toml::from_str::<Config>(&config).unwrap();
         let mut ping_teams = HashMap::new();
@@ -290,6 +302,7 @@ mod tests {
                 nominate: Some(NominateConfig {
                     teams: nominate_teams
                 }),
+                shortcut: Some(ShortcutConfig {allow: vec!["ready".into()]}),
                 prioritize: None,
                 major_change: None,
                 glacier: None,