handlers.rs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  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. macro_rules! handlers {
  21. ($($name:ident = $handler:expr,)*) => {
  22. $(mod $name;)*
  23. mod notification;
  24. pub async fn handle(ctx: &Context, event: &Event) -> Result<(), HandlerError> {
  25. let config = config::get(&ctx.github, event.repo_name()).await;
  26. $(
  27. if let Some(input) = Handler::parse_input(
  28. &$handler, ctx, event, config.as_ref().ok().and_then(|c| c.$name.as_ref()),
  29. ).map_err(HandlerError::Message)? {
  30. let config = match &config {
  31. Ok(config) => config,
  32. Err(e @ ConfigurationError::Missing) => {
  33. return Err(HandlerError::Message(e.to_string()));
  34. }
  35. Err(e @ ConfigurationError::Toml(_)) => {
  36. return Err(HandlerError::Message(e.to_string()));
  37. }
  38. Err(e @ ConfigurationError::Http(_)) => {
  39. return Err(HandlerError::Other(e.clone().into()));
  40. }
  41. };
  42. if let Some(config) = &config.$name {
  43. Handler::handle_input(&$handler, ctx, config, event, input).await.map_err(HandlerError::Other)?;
  44. } else {
  45. return Err(HandlerError::Message(format!(
  46. "The feature `{}` is not enabled in this repository.\n\
  47. To enable it add its section in the `triagebot.toml` \
  48. in the root of the repository.",
  49. stringify!($name)
  50. )));
  51. }
  52. })*
  53. if let Err(e) = notification::handle(ctx, event).await {
  54. log::error!("failed to process event {:?} with notification handler: {:?}", event, e);
  55. }
  56. Ok(())
  57. }
  58. }
  59. }
  60. handlers! {
  61. assign = assign::AssignmentHandler,
  62. relabel = relabel::RelabelHandler,
  63. ping = ping::PingHandler,
  64. nominate = nominate::NominateHandler,
  65. prioritize = prioritize::PrioritizeHandler,
  66. major_change = major_change::MajorChangeHandler,
  67. //tracking_issue = tracking_issue::TrackingIssueHandler,
  68. }
  69. pub struct Context {
  70. pub github: GithubClient,
  71. pub db: DbClient,
  72. pub username: String,
  73. }
  74. pub trait Handler: Sync + Send {
  75. type Input;
  76. type Config;
  77. fn parse_input(
  78. &self,
  79. ctx: &Context,
  80. event: &Event,
  81. config: Option<&Self::Config>,
  82. ) -> Result<Option<Self::Input>, String>;
  83. fn handle_input<'a>(
  84. &self,
  85. ctx: &'a Context,
  86. config: &'a Self::Config,
  87. event: &'a Event,
  88. input: Self::Input,
  89. ) -> BoxFuture<'a, anyhow::Result<()>>;
  90. }