Skip to content

Commit f8307d7

Browse files
committed
add shorcut parser and config
1 parent dbbcb0f commit f8307d7

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

parser/src/command.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub mod ping;
1010
pub mod prioritize;
1111
pub mod relabel;
1212
pub mod second;
13+
pub mod shortcut;
1314

1415
pub fn find_command_start(input: &str, bot: &str) -> Option<usize> {
1516
input.to_ascii_lowercase().find(&format!("@{}", bot))
@@ -24,6 +25,7 @@ pub enum Command<'a> {
2425
Prioritize(Result<prioritize::PrioritizeCommand, Error<'a>>),
2526
Second(Result<second::SecondCommand, Error<'a>>),
2627
Glacier(Result<glacier::GlacierCommand, Error<'a>>),
28+
Shortcut(Result<shortcut::ShortcutCommand, Error<'a>>),
2729
Close(Result<close::CloseCommand, Error<'a>>),
2830
}
2931

@@ -119,6 +121,11 @@ impl<'a> Input<'a> {
119121
Command::Glacier,
120122
&original_tokenizer,
121123
));
124+
success.extend(parse_single_command(
125+
shortcut::ShortcutCommand::parse,
126+
Command::Shortcut,
127+
&original_tokenizer,
128+
));
122129
success.extend(parse_single_command(
123130
close::CloseCommand::parse,
124131
Command::Close,
@@ -182,6 +189,7 @@ impl<'a> Command<'a> {
182189
Command::Prioritize(r) => r.is_ok(),
183190
Command::Second(r) => r.is_ok(),
184191
Command::Glacier(r) => r.is_ok(),
192+
Command::Shortcut(r) => r.is_ok(),
185193
Command::Close(r) => r.is_ok(),
186194
}
187195
}

parser/src/command/shortcut.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//! The shortcut command parser.
2+
//!
3+
//! This can parse predefined shortcut input, single word commands.
4+
//!
5+
//! The grammar is as follows:
6+
//!
7+
//! ```text
8+
//! Command: `@bot ready`, or `@bot author`.
9+
//! ```
10+
11+
use crate::error::Error;
12+
use crate::token::{Token, Tokenizer};
13+
use std::fmt;
14+
15+
#[derive(PartialEq, Eq, Debug)]
16+
pub enum ShortcutCommand {
17+
Ready,
18+
Author,
19+
}
20+
21+
#[derive(PartialEq, Eq, Debug)]
22+
pub enum ParseError {
23+
ExpectedEnd,
24+
}
25+
26+
impl std::error::Error for ParseError {}
27+
28+
impl fmt::Display for ParseError {
29+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30+
match self {
31+
ParseError::ExpectedEnd => write!(f, "expected end of command"),
32+
}
33+
}
34+
}
35+
36+
impl ShortcutCommand {
37+
pub fn parse<'a>(input: &mut Tokenizer<'a>) -> Result<Option<Self>, Error<'a>> {
38+
let mut toks = input.clone();
39+
if let Some(Token::Word("ready")) = toks.peek_token()? {
40+
toks.next_token()?;
41+
if let Some(Token::Dot) | Some(Token::EndOfLine) = toks.peek_token()? {
42+
toks.next_token()?;
43+
*input = toks;
44+
return Ok(Some(ShortcutCommand::Ready));
45+
} else {
46+
return Err(toks.error(ParseError::ExpectedEnd));
47+
}
48+
} else if let Some(Token::Word("author")) = toks.peek_token()? {
49+
toks.next_token()?;
50+
if let Some(Token::Dot) | Some(Token::EndOfLine) = toks.peek_token()? {
51+
toks.next_token()?;
52+
*input = toks;
53+
return Ok(Some(ShortcutCommand::Author));
54+
} else {
55+
return Err(toks.error(ParseError::ExpectedEnd));
56+
}
57+
} else {
58+
return Ok(None);
59+
}
60+
}
61+
}
62+
63+
#[cfg(test)]
64+
fn parse(input: &str) -> Result<Option<ShortcutCommand>, Error<'_>> {
65+
let mut toks = Tokenizer::new(input);
66+
Ok(ShortcutCommand::parse(&mut toks)?)
67+
}
68+
69+
#[test]
70+
fn test_1() {
71+
assert_eq!(parse("ready."), Ok(Some(ShortcutCommand::Ready)),);
72+
}
73+
74+
#[test]
75+
fn test_2() {
76+
assert_eq!(parse("ready"), Ok(Some(ShortcutCommand::Ready)),);
77+
}
78+
79+
#[test]
80+
fn test_3() {
81+
assert_eq!(parse("author"), Ok(Some(ShortcutCommand::Author)),);
82+
}
83+
84+
#[test]
85+
fn test_4() {
86+
use std::error::Error;
87+
assert_eq!(
88+
parse("ready word")
89+
.unwrap_err()
90+
.source()
91+
.unwrap()
92+
.downcast_ref(),
93+
Some(&ParseError::ExpectedEnd),
94+
);
95+
}

src/config.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub(crate) struct Config {
2828
pub(crate) autolabel: Option<AutolabelConfig>,
2929
pub(crate) notify_zulip: Option<NotifyZulipConfig>,
3030
pub(crate) github_releases: Option<GitHubReleasesConfig>,
31+
pub(crate) shortcut: Option<ShortcutConfig>,
3132
}
3233

3334
#[derive(PartialEq, Eq, Debug, serde::Deserialize)]
@@ -81,6 +82,12 @@ pub(crate) struct RelabelConfig {
8182
pub(crate) allow_unauthenticated: Vec<String>,
8283
}
8384

85+
#[derive(PartialEq, Eq, Debug, serde::Deserialize)]
86+
pub(crate) struct ShortcutConfig {
87+
#[serde(default)]
88+
pub(crate) allow: Vec<String>,
89+
}
90+
8491
#[derive(PartialEq, Eq, Debug, serde::Deserialize)]
8592
pub(crate) struct PrioritizeConfig {
8693
pub(crate) label: String,
@@ -248,6 +255,11 @@ mod tests {
248255
release = "T-release"
249256
core = "T-core"
250257
infra = "T-infra"
258+
259+
[shortcut]
260+
allow = [
261+
"ready"
262+
]
251263
"#;
252264
let config = toml::from_str::<Config>(&config).unwrap();
253265
let mut ping_teams = HashMap::new();
@@ -283,6 +295,7 @@ mod tests {
283295
nominate: Some(NominateConfig {
284296
teams: nominate_teams
285297
}),
298+
shortcut: Some(ShortcutConfig {allow: vec!["ready".into()]}),
286299
prioritize: None,
287300
major_change: None,
288301
glacier: None,

0 commit comments

Comments
 (0)