lib.rs 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. #![feature(async_await)]
  2. #![allow(clippy::new_without_default)]
  3. use failure::{Error, ResultExt};
  4. use handlers::HandlerError;
  5. use interactions::ErrorComment;
  6. use std::fmt;
  7. pub mod config;
  8. pub mod github;
  9. pub mod handlers;
  10. pub mod interactions;
  11. pub mod payload;
  12. pub mod team;
  13. pub enum EventName {
  14. IssueComment,
  15. Issue,
  16. Other,
  17. }
  18. impl std::str::FromStr for EventName {
  19. type Err = std::convert::Infallible;
  20. fn from_str(s: &str) -> Result<EventName, Self::Err> {
  21. Ok(match s {
  22. "issue_comment" => EventName::IssueComment,
  23. "issues" => EventName::Issue,
  24. _ => EventName::Other,
  25. })
  26. }
  27. }
  28. impl fmt::Display for EventName {
  29. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  30. write!(
  31. f,
  32. "{}",
  33. match self {
  34. EventName::IssueComment => "issue_comment",
  35. EventName::Issue => "issues",
  36. EventName::Other => "other",
  37. }
  38. )
  39. }
  40. }
  41. #[derive(Debug)]
  42. pub struct WebhookError(Error);
  43. impl From<Error> for WebhookError {
  44. fn from(e: Error) -> WebhookError {
  45. WebhookError(e)
  46. }
  47. }
  48. pub fn deserialize_payload<T: serde::de::DeserializeOwned>(v: &str) -> Result<T, Error> {
  49. Ok(serde_json::from_str(&v).with_context(|_| format!("input: {:?}", v))?)
  50. }
  51. pub async fn webhook(
  52. event: EventName,
  53. payload: String,
  54. ctx: &handlers::Context,
  55. ) -> Result<(), WebhookError> {
  56. let event = match event {
  57. EventName::IssueComment => {
  58. let payload = deserialize_payload::<github::IssueCommentEvent>(&payload)
  59. .context("IssueCommentEvent failed to deserialize")
  60. .map_err(Error::from)?;
  61. log::info!("handling issue comment {:?}", payload);
  62. github::Event::IssueComment(payload)
  63. }
  64. EventName::Issue => {
  65. let payload = deserialize_payload::<github::IssuesEvent>(&payload)
  66. .context("IssuesEvent failed to deserialize")
  67. .map_err(Error::from)?;
  68. log::info!("handling issue event {:?}", payload);
  69. github::Event::Issue(payload)
  70. }
  71. // Other events need not be handled
  72. EventName::Other => {
  73. return Ok(());
  74. }
  75. };
  76. if let Err(err) = handlers::handle(&ctx, &event).await {
  77. match err {
  78. HandlerError::Message(message) => {
  79. if let Some(issue) = event.issue() {
  80. let cmnt = ErrorComment::new(issue, message);
  81. cmnt.post(&ctx.github).await?;
  82. }
  83. }
  84. HandlerError::Other(err) => {
  85. log::error!("handling event failed: {:?}", err);
  86. return Err(WebhookError(failure::err_msg(
  87. "handling failed, error logged",
  88. )));
  89. }
  90. }
  91. }
  92. Ok(())
  93. }