command.rs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. use crate::code_block::ColorCodeBlocks;
  2. use crate::error::Error;
  3. use crate::token::{Token, Tokenizer};
  4. pub mod label;
  5. pub fn find_commmand_start(input: &str, bot: &str) -> Option<usize> {
  6. input.find(&format!("@{}", bot))
  7. }
  8. #[derive(Debug)]
  9. pub enum Command<'a> {
  10. Label(Result<label::LabelCommand, Error<'a>>),
  11. None,
  12. }
  13. #[derive(Debug)]
  14. pub struct Input<'a> {
  15. all: &'a str,
  16. parsed: usize,
  17. code: ColorCodeBlocks,
  18. bot: &'a str,
  19. }
  20. impl<'a> Input<'a> {
  21. pub fn new(input: &'a str, bot: &'a str) -> Input<'a> {
  22. Input {
  23. all: input,
  24. parsed: 0,
  25. code: ColorCodeBlocks::new(input),
  26. bot,
  27. }
  28. }
  29. pub fn parse_command(&mut self) -> Command<'a> {
  30. let start = match find_commmand_start(&self.all[self.parsed..], self.bot) {
  31. Some(pos) => pos,
  32. None => return Command::None,
  33. };
  34. self.parsed += start;
  35. let mut tok = Tokenizer::new(&self.all[self.parsed..]);
  36. assert_eq!(
  37. tok.next_token().unwrap(),
  38. Some(Token::Word(&format!("@{}", self.bot)))
  39. );
  40. let mut success = vec![];
  41. let original_tokenizer = tok.clone();
  42. {
  43. let mut tok = original_tokenizer.clone();
  44. let res = label::LabelCommand::parse(&mut tok);
  45. match res {
  46. Ok(None) => {}
  47. Ok(Some(cmd)) => {
  48. success.push((tok, Command::Label(Ok(cmd))));
  49. }
  50. Err(err) => {
  51. success.push((tok, Command::Label(Err(err))));
  52. }
  53. }
  54. }
  55. if success.len() > 1 {
  56. panic!(
  57. "succeeded parsing {:?} to multiple commands: {:?}",
  58. &self.all[self.parsed..],
  59. success
  60. );
  61. }
  62. if self
  63. .code
  64. .overlaps_code((self.parsed)..(self.parsed + tok.position()))
  65. .is_some()
  66. {
  67. return Command::None;
  68. }
  69. match success.pop() {
  70. Some((mut tok, c)) => {
  71. // if we errored out while parsing the command do not move the input forwards
  72. if c.is_ok() {
  73. self.parsed += tok.position();
  74. }
  75. c
  76. }
  77. None => Command::None,
  78. }
  79. }
  80. }
  81. impl<'a> Command<'a> {
  82. pub fn is_ok(&self) -> bool {
  83. match self {
  84. Command::Label(r) => r.is_ok(),
  85. Command::None => true,
  86. }
  87. }
  88. pub fn is_err(&self) -> bool {
  89. !self.is_ok()
  90. }
  91. pub fn is_none(&self) -> bool {
  92. match self {
  93. Command::None => true,
  94. _ => false,
  95. }
  96. }
  97. }
  98. #[test]
  99. fn errors_outside_command_are_fine() {
  100. let input =
  101. "haha\" unterminated quotes @bot modify labels: +bug. Terminating after the command";
  102. let mut input = Input::new(input, "bot");
  103. assert!(input.parse_command().is_ok());
  104. }
  105. #[test]
  106. fn code_1() {
  107. let input = "`@bot modify labels: +bug.`";
  108. let mut input = Input::new(input, "bot");
  109. assert!(input.parse_command().is_none());
  110. }
  111. #[test]
  112. fn code_2() {
  113. let input = "```
  114. @bot modify labels: +bug.
  115. ```";
  116. let mut input = Input::new(input, "bot");
  117. assert!(input.parse_command().is_none());
  118. }
  119. #[test]
  120. fn move_input_along() {
  121. let input = "@bot modify labels: +bug. Afterwards, delete the world.";
  122. let mut input = Input::new(input, "bot");
  123. let parsed = input.parse_command();
  124. assert!(parsed.is_ok());
  125. assert_eq!(&input.all[input.parsed..], " Afterwards, delete the world.");
  126. }
  127. #[test]
  128. fn move_input_along_1() {
  129. let input = "@bot modify labels\": +bug. Afterwards, delete the world.";
  130. let mut input = Input::new(input, "bot");
  131. assert!(input.parse_command().is_err());
  132. // don't move input along if parsing the command fails
  133. assert_eq!(input.parsed, 0);
  134. }