diff --git a/parser/src/command.rs b/parser/src/command.rs index 9d37311a0..8cee8b4a8 100644 --- a/parser/src/command.rs +++ b/parser/src/command.rs @@ -3,6 +3,7 @@ use crate::error::Error; use crate::token::{Token, Tokenizer}; pub mod assign; +pub mod glacier; pub mod nominate; pub mod ping; pub mod relabel; @@ -17,6 +18,7 @@ pub enum Command<'a> { Assign(Result>), Ping(Result>), Nominate(Result>), + Glacier(Result>), None, } @@ -95,6 +97,11 @@ impl<'a> Input<'a> { Command::Nominate, &original_tokenizer, )); + success.extend(parse_single_command( + glacier::GlacierCommand::parse, + Command::Glacier, + &original_tokenizer, + )); if success.len() > 1 { panic!( @@ -133,6 +140,7 @@ impl<'a> Command<'a> { Command::Assign(r) => r.is_ok(), Command::Ping(r) => r.is_ok(), Command::Nominate(r) => r.is_ok(), + Command::Glacier(r) => r.is_ok(), Command::None => true, } } diff --git a/parser/src/command/glacier.rs b/parser/src/command/glacier.rs new file mode 100644 index 000000000..7e98f44c6 --- /dev/null +++ b/parser/src/command/glacier.rs @@ -0,0 +1,99 @@ +//! The glacier command parser. +//! +//! The grammar is as follows: +//! +//! ```text +//! Command: +//! - `@bot glacier add ?` +//! - `@bot glacier remove` +//! +//! : +//! - "https://play.rust-lang.org/.*" +//! ``` + +use std::fmt; + +use crate::error::Error; +use crate::token::{Token, Tokenizer}; + +#[derive(Debug, PartialEq)] +pub enum GlacierCommand { + Remove, + Add(CodeSource), +} + +#[derive(Debug, PartialEq)] +pub enum CodeSource { + Post, + Url(String), +} + +#[derive(Debug)] +pub enum ParseError { + UnknownSubCommand, +} + +impl std::error::Error for ParseError {} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::UnknownSubCommand => write!(f, "unknown command"), + } + } +} + +impl GlacierCommand { + pub fn parse<'a>(input: &mut Tokenizer<'a>) -> Result, Error<'a>> { + let mut toks = input.clone(); + if let Some(Token::Word("glacier")) = toks.next_token()? { + if let Some(Token::Word("add")) = toks.peek_token()? { + toks.next_token()?; + Ok(Some(Self::Add( + if let Some(Token::Quote(url)) = toks.next_token()? { + CodeSource::Url(url.into()) + } else { + CodeSource::Post + }, + ))) + } else if let Some(Token::Word("remove")) = toks.peek_token()? { + Ok(Some(Self::Remove)) + } else { + Err(toks.error(ParseError::UnknownSubCommand)) + } + } else { + Ok(None) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + fn parse<'a>(input: &'a str) -> Result, Error<'a>> { + let mut toks = Tokenizer::new(input); + Ok(GlacierCommand::parse(&mut toks)?) + } + + #[test] + fn test_remove() { + assert_eq!(parse("glacier remove"), Ok(Some(GlacierCommand::Remove))); + } + + #[test] + fn test_add_post() { + assert_eq!( + parse("glacier add"), + Ok(Some(GlacierCommand::Add(CodeSource::Post))) + ); + } + + #[test] + fn test_add_url() { + assert_eq!( + parse(r#"glacier add "https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a85913678bee64a3262db9a4a59463c2""#), + Ok(Some(GlacierCommand::Add(CodeSource::Url("https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a85913678bee64a3262db9a4a59463c2".into())))) + ); + } +}