handlers.rs 3.5 KB

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