glacier.rs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. //! The glacier command parser.
  2. //!
  3. //! This adds the option to track ICEs. The <code-source> must be in quotes.
  4. //!
  5. //! The grammar is as follows:
  6. //!
  7. //! ```text
  8. //! Command: `@bot glacier <code-source>`
  9. //!
  10. //! <code-source>: any URL that resolves to plain-text Rust code
  11. //! ```
  12. use crate::error::Error;
  13. use crate::token::{Token, Tokenizer};
  14. use std::fmt;
  15. #[derive(PartialEq, Eq, Debug)]
  16. pub struct GlacierCommand {
  17. pub source: String,
  18. }
  19. #[derive(PartialEq, Eq, Debug)]
  20. pub enum ParseError {
  21. NoLink,
  22. InvalidLink,
  23. }
  24. impl std::error::Error for ParseError {}
  25. impl fmt::Display for ParseError {
  26. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  27. match self {
  28. Self::NoLink => write!(f, "no link provided - did you forget the quotes around it?"),
  29. Self::InvalidLink => write!(f, "invalid link - must be from a playground gist"),
  30. }
  31. }
  32. }
  33. impl GlacierCommand {
  34. pub fn parse<'a>(input: &mut Tokenizer<'a>) -> Result<Option<GlacierCommand>, Error<'a>> {
  35. let mut toks = input.clone();
  36. if let Some(Token::Word("glacier")) = toks.peek_token()? {
  37. toks.next_token()?;
  38. match toks.next_token()? {
  39. Some(Token::Quote(s)) => {
  40. let source = s.to_owned();
  41. if source.starts_with("https://gist.github.com/") {
  42. return Ok(Some(GlacierCommand { source }));
  43. } else {
  44. return Err(toks.error(ParseError::InvalidLink));
  45. }
  46. }
  47. Some(Token::Word(_)) => {
  48. return Err(toks.error(ParseError::InvalidLink));
  49. }
  50. _ => {
  51. return Err(toks.error(ParseError::NoLink));
  52. }
  53. }
  54. } else {
  55. Ok(None)
  56. }
  57. }
  58. }
  59. #[cfg(test)]
  60. mod test {
  61. use super::*;
  62. fn parse<'a>(input: &'a str) -> Result<Option<GlacierCommand>, Error<'a>> {
  63. let mut toks = Tokenizer::new(input);
  64. Ok(GlacierCommand::parse(&mut toks)?)
  65. }
  66. #[test]
  67. fn glacier_empty() {
  68. use std::error::Error;
  69. assert_eq!(
  70. parse("glacier")
  71. .unwrap_err()
  72. .source()
  73. .unwrap()
  74. .downcast_ref(),
  75. Some(&ParseError::NoLink),
  76. );
  77. }
  78. #[test]
  79. fn glacier_invalid() {
  80. use std::error::Error;
  81. assert_eq!(
  82. parse("glacier hello")
  83. .unwrap_err()
  84. .source()
  85. .unwrap()
  86. .downcast_ref(),
  87. Some(&ParseError::InvalidLink),
  88. );
  89. }
  90. #[test]
  91. fn glacier_valid() {
  92. assert_eq!(
  93. parse(
  94. r#"glacier "https://gist.github.com/rust-play/89d6c8a2398dd2dd5fcb7ef3e8109c7b""#
  95. ),
  96. Ok(Some(GlacierCommand {
  97. source: "https://gist.github.com/rust-play/89d6c8a2398dd2dd5fcb7ef3e8109c7b".into()
  98. }))
  99. );
  100. }
  101. }