handlers.rs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. use crate::config::{self, ConfigurationError};
  2. use crate::github::{Event, GithubClient};
  3. use futures::future::BoxFuture;
  4. use octocrab::Octocrab;
  5. use std::fmt;
  6. use tokio_postgres::Client as DbClient;
  7. #[derive(Debug)]
  8. pub enum HandlerError {
  9. Message(String),
  10. Other(anyhow::Error),
  11. }
  12. impl std::error::Error for HandlerError {}
  13. impl fmt::Display for HandlerError {
  14. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  15. match self {
  16. HandlerError::Message(msg) => write!(f, "{}", msg),
  17. HandlerError::Other(_) => write!(f, "An internal error occurred."),
  18. }
  19. }
  20. }
  21. mod notification;
  22. mod rustc_commits;
  23. macro_rules! handlers {
  24. ($($name:ident = $handler:expr,)*) => {
  25. $(mod $name;)*
  26. pub async fn handle(ctx: &Context, event: &Event) -> Result<(), HandlerError> {
  27. let config = config::get(&ctx.github, event.repo_name()).await;
  28. $(
  29. if let Some(input) = Handler::parse_input(
  30. &$handler, ctx, event, config.as_ref().ok().and_then(|c| c.$name.as_ref()),
  31. ).map_err(HandlerError::Message)? {
  32. let config = match &config {
  33. Ok(config) => config,
  34. Err(e @ ConfigurationError::Missing) => {
  35. return Err(HandlerError::Message(e.to_string()));
  36. }
  37. Err(e @ ConfigurationError::Toml(_)) => {
  38. return Err(HandlerError::Message(e.to_string()));
  39. }
  40. Err(e @ ConfigurationError::Http(_)) => {
  41. return Err(HandlerError::Other(e.clone().into()));
  42. }
  43. };
  44. if let Some(config) = &config.$name {
  45. Handler::handle_input(&$handler, ctx, config, event, input).await.map_err(HandlerError::Other)?;
  46. } else {
  47. return Err(HandlerError::Message(format!(
  48. "The feature `{}` is not enabled in this repository.\n\
  49. To enable it add its section in the `triagebot.toml` \
  50. in the root of the repository.",
  51. stringify!($name)
  52. )));
  53. }
  54. })*
  55. if let Err(e) = notification::handle(ctx, event).await {
  56. log::error!("failed to process event {:?} with notification handler: {:?}", event, e);
  57. }
  58. if let Err(e) = rustc_commits::handle(ctx, event).await {
  59. log::error!("failed to process event {:?} with rustc_commits handler: {:?}", event, e);
  60. }
  61. Ok(())
  62. }
  63. }
  64. }
  65. handlers! {
  66. assign = assign::AssignmentHandler,
  67. relabel = relabel::RelabelHandler,
  68. ping = ping::PingHandler,
  69. nominate = nominate::NominateHandler,
  70. prioritize = prioritize::PrioritizeHandler,
  71. major_change = major_change::MajorChangeHandler,
  72. //tracking_issue = tracking_issue::TrackingIssueHandler,
  73. glacier = glacier::GlacierHandler,
  74. autolabel = autolabel::AutolabelHandler,
  75. notify_zulip = notify_zulip::NotifyZulipHandler,
  76. }
  77. pub struct Context {
  78. pub github: GithubClient,
  79. pub db: DbClient,
  80. pub username: String,
  81. pub octocrab: Octocrab,
  82. }
  83. pub trait Handler: Sync + Send {
  84. type Input;
  85. type Config;
  86. fn parse_input(
  87. &self,
  88. ctx: &Context,
  89. event: &Event,
  90. config: Option<&Self::Config>,
  91. ) -> Result<Option<Self::Input>, String>;
  92. fn handle_input<'a>(
  93. &self,
  94. ctx: &'a Context,
  95. config: &'a Self::Config,
  96. event: &'a Event,
  97. input: Self::Input,
  98. ) -> BoxFuture<'a, anyhow::Result<()>>;
  99. }