command.rs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. use crate::code_block::ColorCodeBlocks;
  2. use crate::error::Error;
  3. use crate::token::{Token, Tokenizer};
  4. pub mod assign;
  5. pub mod close;
  6. pub mod glacier;
  7. pub mod nominate;
  8. pub mod ping;
  9. pub mod prioritize;
  10. pub mod relabel;
  11. pub mod second;
  12. pub fn find_command_start(input: &str, bot: &str) -> Option<usize> {
  13. input.find(&format!("@{}", bot))
  14. }
  15. #[derive(Debug, PartialEq)]
  16. pub enum Command<'a> {
  17. Relabel(Result<relabel::RelabelCommand, Error<'a>>),
  18. Assign(Result<assign::AssignCommand, Error<'a>>),
  19. Ping(Result<ping::PingCommand, Error<'a>>),
  20. Nominate(Result<nominate::NominateCommand, Error<'a>>),
  21. Prioritize(Result<prioritize::PrioritizeCommand, Error<'a>>),
  22. Second(Result<second::SecondCommand, Error<'a>>),
  23. Glacier(Result<glacier::GlacierCommand, Error<'a>>),
  24. Close(Result<close::CloseCommand, Error<'a>>),
  25. }
  26. #[derive(Debug)]
  27. pub struct Input<'a> {
  28. all: &'a str,
  29. parsed: usize,
  30. code: ColorCodeBlocks,
  31. bot: &'a str,
  32. }
  33. fn parse_single_command<'a, T, F, M>(
  34. parse: F,
  35. mapper: M,
  36. tokenizer: &Tokenizer<'a>,
  37. ) -> Option<(Tokenizer<'a>, Command<'a>)>
  38. where
  39. F: FnOnce(&mut Tokenizer<'a>) -> Result<Option<T>, Error<'a>>,
  40. M: FnOnce(Result<T, Error<'a>>) -> Command<'a>,
  41. T: std::fmt::Debug,
  42. {
  43. let mut tok = tokenizer.clone();
  44. let res = parse(&mut tok);
  45. log::info!("parsed {:?} command: {:?}", std::any::type_name::<T>(), res);
  46. match res {
  47. Ok(None) => None,
  48. Ok(Some(v)) => Some((tok, mapper(Ok(v)))),
  49. Err(err) => Some((tok, mapper(Err(err)))),
  50. }
  51. }
  52. impl<'a> Input<'a> {
  53. pub fn new(input: &'a str, bot: &'a str) -> Input<'a> {
  54. Input {
  55. all: input,
  56. parsed: 0,
  57. code: ColorCodeBlocks::new(input),
  58. bot,
  59. }
  60. }
  61. fn parse_command(&mut self) -> Option<Command<'a>> {
  62. let mut tok = Tokenizer::new(&self.all[self.parsed..]);
  63. assert_eq!(
  64. tok.next_token().unwrap(),
  65. Some(Token::Word(&format!("@{}", self.bot)))
  66. );
  67. log::info!("identified potential command");
  68. let mut success = vec![];
  69. let original_tokenizer = tok.clone();
  70. success.extend(parse_single_command(
  71. relabel::RelabelCommand::parse,
  72. Command::Relabel,
  73. &original_tokenizer,
  74. ));
  75. success.extend(parse_single_command(
  76. assign::AssignCommand::parse,
  77. Command::Assign,
  78. &original_tokenizer,
  79. ));
  80. success.extend(parse_single_command(
  81. ping::PingCommand::parse,
  82. Command::Ping,
  83. &original_tokenizer,
  84. ));
  85. success.extend(parse_single_command(
  86. nominate::NominateCommand::parse,
  87. Command::Nominate,
  88. &original_tokenizer,
  89. ));
  90. success.extend(parse_single_command(
  91. prioritize::PrioritizeCommand::parse,
  92. Command::Prioritize,
  93. &original_tokenizer,
  94. ));
  95. success.extend(parse_single_command(
  96. second::SecondCommand::parse,
  97. Command::Second,
  98. &original_tokenizer,
  99. ));
  100. success.extend(parse_single_command(
  101. glacier::GlacierCommand::parse,
  102. Command::Glacier,
  103. &original_tokenizer,
  104. ));
  105. success.extend(parse_single_command(
  106. close::CloseCommand::parse,
  107. Command::Close,
  108. &original_tokenizer,
  109. ));
  110. if success.len() > 1 {
  111. panic!(
  112. "succeeded parsing {:?} to multiple commands: {:?}",
  113. &self.all[self.parsed..],
  114. success
  115. );
  116. }
  117. if self
  118. .code
  119. .overlaps_code((self.parsed)..(self.parsed + tok.position()))
  120. .is_some()
  121. {
  122. log::info!("command overlaps code; code: {:?}", self.code);
  123. return None;
  124. }
  125. let (mut tok, c) = success.pop()?;
  126. // if we errored out while parsing the command do not move the input forwards
  127. self.parsed += if c.is_ok() {
  128. tok.position()
  129. } else {
  130. self.bot.len() + 1
  131. };
  132. Some(c)
  133. }
  134. }
  135. impl<'a> Iterator for Input<'a> {
  136. type Item = Command<'a>;
  137. fn next(&mut self) -> Option<Command<'a>> {
  138. loop {
  139. let start = find_command_start(&self.all[self.parsed..], self.bot)?;
  140. self.parsed += start;
  141. if let Some(command) = self.parse_command() {
  142. return Some(command);
  143. }
  144. self.parsed += self.bot.len() + 1;
  145. }
  146. }
  147. }
  148. impl<'a> Command<'a> {
  149. pub fn is_ok(&self) -> bool {
  150. match self {
  151. Command::Relabel(r) => r.is_ok(),
  152. Command::Assign(r) => r.is_ok(),
  153. Command::Ping(r) => r.is_ok(),
  154. Command::Nominate(r) => r.is_ok(),
  155. Command::Prioritize(r) => r.is_ok(),
  156. Command::Second(r) => r.is_ok(),
  157. Command::Glacier(r) => r.is_ok(),
  158. Command::Close(r) => r.is_ok(),
  159. }
  160. }
  161. pub fn is_err(&self) -> bool {
  162. !self.is_ok()
  163. }
  164. }
  165. #[test]
  166. fn errors_outside_command_are_fine() {
  167. let input =
  168. "haha\" unterminated quotes @bot modify labels: +bug. Terminating after the command";
  169. let mut input = Input::new(input, "bot");
  170. assert!(input.next().unwrap().is_ok());
  171. }
  172. #[test]
  173. fn code_1() {
  174. let input = "`@bot modify labels: +bug.`";
  175. let mut input = Input::new(input, "bot");
  176. assert!(input.next().is_none());
  177. }
  178. #[test]
  179. fn code_2() {
  180. let input = "```
  181. @bot modify labels: +bug.
  182. ```";
  183. let mut input = Input::new(input, "bot");
  184. assert!(input.next().is_none());
  185. }
  186. #[test]
  187. fn edit_1() {
  188. let input_old = "@bot modify labels: +bug.";
  189. let mut input_old = Input::new(input_old, "bot");
  190. let input_new = "Adding labels: @bot modify labels: +bug. some other text";
  191. let mut input_new = Input::new(input_new, "bot");
  192. assert_eq!(input_old.next(), input_new.next());
  193. }
  194. #[test]
  195. fn edit_2() {
  196. let input_old = "@bot modify label: +bug.";
  197. let mut input_old = Input::new(input_old, "bot");
  198. let input_new = "@bot modify labels: +bug.";
  199. let mut input_new = Input::new(input_new, "bot");
  200. assert_ne!(input_old.next(), input_new.next());
  201. }
  202. #[test]
  203. fn move_input_along() {
  204. let input = "@bot modify labels: +bug. Afterwards, delete the world.";
  205. let mut input = Input::new(input, "bot");
  206. assert!(input.next().unwrap().is_ok());
  207. assert_eq!(&input.all[input.parsed..], " Afterwards, delete the world.");
  208. }
  209. #[test]
  210. fn move_input_along_1() {
  211. let input = "@bot modify labels\": +bug. Afterwards, delete the world.";
  212. let mut input = Input::new(input, "bot");
  213. assert!(input.next().unwrap().is_err());
  214. // don't move input along if parsing the command fails
  215. assert_eq!(&input.all[..input.parsed], "@bot");
  216. }